using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using System.Data.SqlClient;
using System.Globalization;
using System.Collections;
using System.IO;
using net.windward.api.csharp;
using net.windward.format;
//using WindwardArrow.net.windward.arrow.schedule.run;
using ArrowScheduleCore;
using DevExpress.Web.ASPxEditors;
using DevExpress.Web.ASPxScheduler.Controls;
using System.Net;
using System.Collections.ObjectModel;
using DevExpress.Web.ASPxTabControl;
using DevExpress.Web.ASPxMenu;
using DevExpress.Web.ASPxGridView;
using Microsoft.SharePoint.WebControls;
using System.Drawing;
using WC = System.Web.UI.WebControls;
using Microsoft.SharePoint.Administration;
using System.Configuration;
using net.windward.util;
using Kailua.net.windward.utils.datetime;
using DevExpress.Data;
using log4net;
using Kailua.net.windward.utils;

namespace ArrowWebParts
{
    /// <summary>
    /// A wizard that walks user through scheduling a windward report in sharepoint.
    /// </summary>
    public class SchedulerWebPart : WebPart
    {
        #region Controls

        /// <summary>
        /// Header style text.
        /// </summary>
        private static Style headerStyle;
        /// <summary>
        /// Normal style text.
        /// </summary>
        private static Style normalStyle;
        /// <summary>
        /// Normal size, bold text.
        /// </summary>
        private static Style normalBoldStyle;
        /// <summary>
        /// Displays status information contained in infoMessage;
        /// </summary>
        private Label lblStatus;
        /// <summary>
        /// For selecting the template
        /// </summary>
        private ITemplateSelector templateSelector;
        /// <summary>
        /// for selecting the values of the parameters
        /// </summary>
        private ParameterValueSelector parmSelector;
        /// <summary>
        /// For setting the schedule
        /// </summary>
        private ScheduleSelector scheduleSelector;
        /// <summary>
        /// For selecting the output properties
        /// </summary>
        private IOutputSelector outputSelector;
        /// <summary>
        /// The review page control.  Review content is added here to allow facilitate UI changes.
        /// </summary>
        private Control reviewControl;
        /// <summary>
        /// Indicates if the scheduler UI should be restarted.
        /// </summary>
        private HiddenField hidStartOver;
        /// <summary>
        /// Indicates the report was successfully scheduled.
        /// </summary>
        private ASPxLabel lblSuccess;

        #endregion Controls


        #region Fields

        /// <summary>
        /// the overview tab index
        /// </summary>
        private const int STEP_OVERVIEW = 0;
        /// <summary>
        /// the wizard step index of template selection
        /// </summary>
        private const int STEP_TEMPLATE_SELECTION = 1;
        /// <summary>
        /// the wizard step index of parameter value selection
        /// </summary>
        private const int STEP_PARAMETER_SELECTION = 2;
        /// <summary>
        /// the wizard step index of schedule selection
        /// </summary>
        private const int STEP_SCHEDULE_SELECTION = 3;
        /// <summary>
        /// the output selection tab index
        /// </summary>
        private const int STEP_OUTPUT_SELECTION = 4;
        /// <summary>
        /// the review tab index
        /// </summary>
        private const int STEP_REVIEW = 5;
        /// <summary>
        /// The head string for error messages.
        /// </summary>
        private static readonly string ERROR_HEAD = "<p><b><span style=\"color: red\">ERROR:&nbsp;</span>";
        /// <summary>
        /// The tail string for error messages.
        /// </summary>
        private static readonly string ERROR_TAIL = "</b></p>";
        /// <summary>
        /// Indicates if the product is licensed.  All methods return immediately if not.
        /// </summary>
        private bool isLicensed = false;
        
        // image path strings
        /// <summary>
        /// The image directory
        /// </summary>
        private static readonly string IMAGE_DIR = "_layouts/1033/images/arrow/";
        /// <summary>
        /// The overview step image path.
        /// </summary>
        private static readonly string IMAGE_OVERVIEW_PATH = IMAGE_DIR + "overviewStepAssemble.png";
        /// <summary>
        /// The report definition step image path.
        /// </summary>
        private static readonly string IMAGE_REPORTDEF_PATH = IMAGE_DIR + "assembleStep.png";
        /// <summary>
        /// The variable selection step image path.
        /// </summary>
        private static readonly string IMAGE_VARIABLE_PATH = IMAGE_DIR + "dataStep.png";
        /// <summary>
        /// The schedule selection step image path.
        /// </summary>
        private static readonly string IMAGE_SCHEDULE_PATH = IMAGE_DIR + "scheduleStep.png";
        /// <summary>
        /// The output selection step image path.
        /// </summary>
        private static readonly string IMAGE_OUTPUT_PATH = IMAGE_DIR + "outputStep.png";
        /// <summary>
        /// The review step image path.
        /// </summary>
        private static readonly string IMAGE_REVIEW_PATH = IMAGE_DIR + "reviewStep_final.png";

        private static ILog log = LogManager.GetLogger(typeof(SchedulerWebPart));

        #endregion Fields


        #region Wizard UI

        /// <summary>
        /// The main content of this web part, a wizard.
        /// </summary>
        private Wizard schedWizard;
        /// <summary>
        /// The previous step used in the wizard.
        /// </summary>
        private int prevStep = 0;

        #endregion Wizard UI


        #region Explicit Interface Overrides

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            try
            {
                if (log.IsDebugEnabled)
                    log.Debug("creating child controls");

                try
                {
                    // read in license from web.config
                    ArrowUtils.InitializeArrow(string.Concat(Page.Request.PhysicalApplicationPath, "\\web.config"));
                    isLicensed = true;
                }
                catch (LicenseException lex)
                {
                    ArrowUtils.LogFatalException(log, lex);
                    Controls.Add(new LiteralControl(string.Format("License error: {0}", lex.Message)));
                    return;
                }

                headerStyle = new Style();
                headerStyle.Font.Name = "Verdana";
                headerStyle.Font.Size = FontUnit.Parse("14pt");

                normalStyle = new Style();
                normalStyle.Font.Name = "Verdana";
                normalStyle.Font.Size = FontUnit.Parse("10pt");

                normalBoldStyle = new Style();
                normalBoldStyle.Font.Name = "Verdana";
                normalBoldStyle.Font.Size = FontUnit.Parse("12pt");
                normalBoldStyle.Font.Bold = true;

                // start over switch
                hidStartOver = new HiddenField();
                hidStartOver.Value = false.ToString();
                Controls.Add(hidStartOver);

                // title & status
                lblStatus = new Label();
                resetStatus();
                Controls.Add(lblStatus);

                #region Wizard-Style GUI

                // add the schedule wizard
                schedWizard = new Wizard();
                schedWizard.BorderStyle = BorderStyle.Solid;
                schedWizard.CellPadding = 10;
                //schedWizard.Width = 550;
                //schedWizard.Height = 400;
                schedWizard.BorderColor = Color.FromName("Black");
                schedWizard.BorderWidth = 1;
                schedWizard.StartNextButtonText = "Next";
                schedWizard.StepNextButtonText = "Next";
                schedWizard.NextButtonClick += new WizardNavigationEventHandler(schedWizard_NextButtonClick);
                schedWizard.StepPreviousButtonText = "Previous";
                schedWizard.FinishPreviousButtonText = "Previous";
                schedWizard.FinishCompleteButtonText = "Schedule Report";
                schedWizard.FinishCompleteButtonStyle.BackColor = Color.FromName("Orange");
                schedWizard.FinishButtonClick += new WizardNavigationEventHandler(schedWizard_FinishButtonClick);
                schedWizard.SideBarButtonClick += new WizardNavigationEventHandler(schedWizard_SideBarButtonClick);
                schedWizard.PreviousButtonClick += new WizardNavigationEventHandler(schedWizard_PreviousButtonClick);
                schedWizard.DisplaySideBar = false;

                // overview step
                WizardStep overviewStep = new WizardStep();
                overviewStep.ID = "Overview";
                overviewStep.Title = "Overview";
                AddOverviewControl(overviewStep);
                schedWizard.WizardSteps.Add(overviewStep);

                // template selection step
                WizardStep templateSelectionStep = new WizardStep();
                templateSelectionStep.ID = "GetTemplate";
                templateSelectionStep.Title = "Template Selector";
                templateSelectionStep.Controls.Add((Control)(templateSelector = new TemplateSelector(Page)));
                schedWizard.WizardSteps.Add(templateSelectionStep);

                // parameter selection step
                WizardStep parameterSelectionStep = new WizardStep();
                parameterSelectionStep.ID = "GetParameters";
                parameterSelectionStep.Title = "Parameter Selection";
                parameterSelectionStep.Controls.Add((Control)(parmSelector = new ParameterValueSelector()));
                schedWizard.WizardSteps.Add(parameterSelectionStep);

                // schedule selection step
                WizardStep scheduleSelectionStep = new WizardStep();
                scheduleSelectionStep.ID = "DoSchedule";
                scheduleSelectionStep.Title = "Schedule The Report";
                scheduleSelectionStep.Controls.Add(scheduleSelector = new ScheduleSelector());
                // sched sel init here because it needs viewstate before event hdlrs
                if (ViewState["reportName"] != null)
                    scheduleSelector.outName = (string)ViewState["reportName"];
                if (ViewState["outputLibGuid"] != null)
                    scheduleSelector.outLibGuid = new Guid((string)ViewState["outputLibGuid"]);
                if (ViewState["templateGuid"] != null)
                    scheduleSelector.tGuid = new Guid((string)ViewState["templateGuid"]);
                if (ViewState["outputFormat"] != null)
                    scheduleSelector.outFmt = (string)ViewState["outputFormat"];
                schedWizard.WizardSteps.Add(scheduleSelectionStep);

                // output selection step
                WizardStep outputSelectionStep = new WizardStep();
                outputSelectionStep.ID = "OutputSelectionStep";
                outputSelectionStep.Title = "Select Output";
                outputSelectionStep.Controls.Add((Control)(outputSelector = new OutputSelector()));
                schedWizard.WizardSteps.Add(outputSelectionStep);

                // review step
                WizardStep reviewStep = new WizardStep();
                reviewStep.ID = "ReviewStep";
                reviewStep.Title = "Review Selections";
                // this is how tabs do it
                addReviewControl(reviewStep);
                // we probably want to parameterize this method or make
                //createReviewWizardControls();
                schedWizard.WizardSteps.Add(reviewStep);

                Controls.Add(schedWizard);

                #endregion Wizard-Style GUI

                if (ViewState["prevStep"] != null)
                    prevStep = (int)ViewState["prevStep"];
                else
                    prevStep = STEP_TEMPLATE_SELECTION;
            }
            catch (Exception ex)
            {
                ArrowUtils.LogFatalException(log, ex);
                Controls.Clear();
                Controls.Add(new LiteralControl("<p>There was an error creating the scheduler web part.</p>"));
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            try
            {
                if (log.IsDebugEnabled)
                    log.Debug("handling prerender");

                if (!isLicensed)
                    return;

                // load the parameter selection if needed
                if (schedWizard.ActiveStepIndex == STEP_PARAMETER_SELECTION &&
                    prevStep == STEP_TEMPLATE_SELECTION)
                {
                    parmSelector.ReportAssemblyGuid = templateSelector.ReportAssemblyGuid;
                    parmSelector.templateSiteGuid = new Guid(templateSelector.ReportSiteGuid);
                    parmSelector.templateGuid = new Guid(templateSelector.ReportAssemblyGuid);
                    parmSelector.updateVarTable();
                }

                // fill out the review info
                generateReviewContent();
            }
            catch (Exception ex)
            {
                ArrowUtils.LogFatalException(log, ex);
                Controls.Clear();
                Controls.Add(new LiteralControl("<p>There was an error in the scheduler web part.</p>"));
            }
        }

        #endregion Explicit Interface Overrides


        #region Event Handlers

        void btnSchedule_Click(object sender, EventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling schedule button click");
            scheduleReport();
        }

        void schedWizard_PreviousButtonClick(object sender, WizardNavigationEventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling prev button click");
            schedWizard_NextButtonClick(sender, e);
        }

        /// <summary>
        /// Does some validation and initialization tasks when advancing a step.
        /// Same as schedWizard_nextButtonClick
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void schedWizard_SideBarButtonClick(object sender, WizardNavigationEventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling sidebar click");
            schedWizard_NextButtonClick(sender, e);
        }

        /// <summary>
        /// Does some validation and initialization tasks when advancing a step.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void schedWizard_NextButtonClick(object sender, WizardNavigationEventArgs e)
        {
            try
            {
                if (log.IsDebugEnabled)
                    log.Debug("handling next button click");

                switch (e.CurrentStepIndex)
                {
                    case STEP_OVERVIEW: /* overview step */
                        if (log.IsDebugEnabled)
                            log.Debug("moving from overview step");
                        ViewState["prevStep"] = STEP_OVERVIEW;
                        prevStep = STEP_OVERVIEW;
                        break;
                    case STEP_TEMPLATE_SELECTION:/* template selection */
                        if (log.IsDebugEnabled)
                            log.Debug("moving from template selection step");
                        ViewState["prevStep"] = STEP_TEMPLATE_SELECTION;
                        prevStep = STEP_TEMPLATE_SELECTION;
                        if (!isValidTemplateSelection() && e.NextStepIndex > STEP_TEMPLATE_SELECTION)
                        {
                            e.Cancel = true;
                        }
                        else if (templateSelector.IsTemplateAccessDenied)
                        {
                            addStatusError("You do not have permission to access the specified template or report definition.");
                            e.Cancel = true;
                            break;
                        }
                        else
                        {
                            UpdateViewStateVariables();
                        }

                        // check if we can skip parameter selection
                        if (isValidTemplateSelection() && e.NextStepIndex == STEP_PARAMETER_SELECTION)
                        {
                            // check if any vars
                            Guid rptDefGuid = new Guid(templateSelector.ReportAssemblyGuid);
                            Guid rptSiteGuid = new Guid(templateSelector.ReportSiteGuid);
                            string errMsg = null;
                            ReportDefinition rptDef = ArrowUtils.GetReportDefFromFile(rptSiteGuid, rptDefGuid, out errMsg);
                            int numVars = 0;
                            if (rptDef.variableNameArray != null)
                                numVars = ArrowUtils.GetNumVarsForDisplay(rptDef);
                            if (numVars < 1)
                            {
                                if (log.IsDebugEnabled)
                                    log.Debug("detected no vars; moving to schedule selection step");
                                schedWizard.ActiveStepIndex = STEP_SCHEDULE_SELECTION;
                            }
                        }
                        break;
                    case STEP_PARAMETER_SELECTION:/* parameter value selection */
                        if (log.IsDebugEnabled)
                            log.Debug("moving from parameter selection step");
                        ViewState["prevStep"] = STEP_PARAMETER_SELECTION;
                        prevStep = STEP_PARAMETER_SELECTION;
                        UpdateViewStateVariables();
                        break;
                    case STEP_SCHEDULE_SELECTION:/* schedule selection */
                        if (log.IsDebugEnabled)
                            log.Debug("moving from schedule selection step");
                        ViewState["prevStep"] = STEP_SCHEDULE_SELECTION;
                        prevStep = STEP_SCHEDULE_SELECTION;
                        UpdateViewStateVariables();
                        break;
                    case STEP_OUTPUT_SELECTION: /* output selection */
                        if (log.IsDebugEnabled)
                            log.Debug("moving from output selection step");
                        ViewState["prevStep"] = STEP_OUTPUT_SELECTION;
                        prevStep = STEP_OUTPUT_SELECTION;
                        UpdateViewStateVariables();
                        break;
                    case STEP_REVIEW: /* review step */
                        if (log.IsDebugEnabled)
                            log.Debug("moving from review step");
                        ViewState["prevStep"] = STEP_REVIEW;
                        prevStep = STEP_REVIEW;
                        UpdateViewStateVariables();
                        break;
                    default:
                        Exception ex = new Exception("Invalid Wizard Step: " + e.CurrentStepIndex.ToString());
                        ArrowUtils.LogFatalException(log, ex);
                        throw ex;
                }
            }
            catch (Exception ex)
            {
                ArrowUtils.LogFatalException(log, ex);
                Controls.Clear();
                Controls.Add(new LiteralControl("<p>There was an error changing steps.</p>"));
            }
        }

        void schedWizard_FinishButtonClick(object sender, WizardNavigationEventArgs e)
        {
            try
            {
                if (log.IsDebugEnabled)
                    log.Debug("handling finish button click");

                if (hidStartOver.Value != null && hidStartOver.Value.Equals(true.ToString()))
                {
                    // start over
                    Context.Response.Redirect(Context.Request.Path);
                }

                scheduleReport();
            }
            catch (Exception ex)
            {
                ArrowUtils.LogFatalException(log, ex);
                Controls.Clear();
                Controls.Add(new LiteralControl("<p>There was an error scheduling the report.</p>"));
            }
        }

        #endregion Event Handlers


        #region Helper & Utility Methods

        /// <summary>
        /// Creates the overview control and adds it to the container's Controls collection.
        /// </summary>
        private static void AddOverviewControl(Control container)
        {
            Table layoutTable = createEmptyTable(11, 3, 40, HorizontalAlign.Left);
            layoutTable.CellPadding = 10;

            // step 1 select asm
            ASPxImage imgStep1 = new ASPxImage();
            imgStep1.ImageUrl = IMAGE_REPORTDEF_PATH;
            layoutTable.Rows[1].Cells[0].Controls.Add(imgStep1);
            ASPxLabel lblStep1 = new ASPxLabel();
            lblStep1.Text = "Step 1:";
            lblStep1.ApplyStyle(normalBoldStyle);
            layoutTable.Rows[1].Cells[1].Controls.Add(lblStep1);
            ASPxLabel lblInstruct1 = new ASPxLabel();
            lblInstruct1.Text = "Pick a report definition or template file.";
            lblInstruct1.ApplyStyle(normalStyle);
            layoutTable.Rows[1].Cells[2].Controls.Add(lblInstruct1);

            // step 2 set vars
            ASPxImage imgStep2 = new ASPxImage();
            imgStep2.ImageUrl = IMAGE_VARIABLE_PATH;
            layoutTable.Rows[3].Cells[0].Controls.Add(imgStep2);
            ASPxLabel lblStep2 = new ASPxLabel();
            lblStep2.Text = "Step 2:";
            lblStep2.ApplyStyle(normalBoldStyle);
            layoutTable.Rows[3].Cells[1].Controls.Add(lblStep2);
            ASPxLabel lblInstruct2 = new ASPxLabel();
            lblInstruct2.Text = "Input or select report variable values.";
            lblInstruct2.ApplyStyle(normalStyle);
            layoutTable.Rows[3].Cells[2].Controls.Add(lblInstruct2);

            // step 3 set sched
            ASPxImage imgStep3 = new ASPxImage();
            imgStep3.ImageUrl = IMAGE_SCHEDULE_PATH;
            layoutTable.Rows[5].Cells[0].Controls.Add(imgStep3);
            ASPxLabel lblStep3 = new ASPxLabel();
            lblStep3.Text = "Step 3:";
            lblStep3.ApplyStyle(normalBoldStyle);
            layoutTable.Rows[5].Cells[1].Controls.Add(lblStep3);
            ASPxLabel lblInstruct3 = new ASPxLabel();
            lblInstruct3.Text = "Select the schedule settings.";
            lblInstruct3.ApplyStyle(normalStyle);
            layoutTable.Rows[5].Cells[2].Controls.Add(lblInstruct3);

            // step 4 set output
            ASPxImage imgStep4 = new ASPxImage();
            imgStep4.ImageUrl = IMAGE_OUTPUT_PATH;
            layoutTable.Rows[7].Cells[0].Controls.Add(imgStep4);
            ASPxLabel lblStep4 = new ASPxLabel();
            lblStep4.Text = "Step 4:";
            lblStep4.ApplyStyle(normalBoldStyle);
            layoutTable.Rows[7].Cells[1].Controls.Add(lblStep4);
            ASPxLabel lblInstruct4 = new ASPxLabel();
            lblInstruct4.Text = "Pick a document library to save the report in.";
            lblInstruct4.ApplyStyle(normalStyle);
            layoutTable.Rows[7].Cells[2].Controls.Add(lblInstruct4);

            // finalize and go
            ASPxImage imgStepR = new ASPxImage();
            imgStepR.ImageUrl = IMAGE_REVIEW_PATH;
            layoutTable.Rows[9].Cells[0].Controls.Add(imgStepR);
            ASPxLabel lblStepR = new ASPxLabel();
            lblStepR.Text = "Finalize\nand\nGo:";
            lblStepR.ApplyStyle(normalBoldStyle);
            layoutTable.Rows[9].Cells[1].HorizontalAlign = HorizontalAlign.Center;
            layoutTable.Rows[9].Cells[1].Controls.Add(lblStepR);
            ASPxLabel lblInstructR = new ASPxLabel();
            lblInstructR.Text = "Review details and schedule the report";
            lblInstructR.ApplyStyle(normalStyle);
            layoutTable.Rows[9].Cells[2].Controls.Add(lblInstructR);

            container.Controls.Add(layoutTable);
        }

        /// <summary>
        /// Creates the review control and adds it to the container's Controls collection.
        /// In a tab-style gui, display the schedule button here.
        /// </summary>
        public void addReviewControl(Control container)
        {
            //Table layoutTable = createEmptyTable(16, 1, 33, HorizontalAlign.Center);
            Table layoutTable = createEmptyTable(14, 1, 33, HorizontalAlign.Center);

            // display step header
            Label lblHeadInfo = new Label { Text = "Review details and schedule the report \n" };
            lblHeadInfo.ApplyStyle(ArrowUtils.headerStyle());
            Label lblAsm = new Label { Text = "Assembly:" };
            Label lblDate = new Label { Text = "Schedule:" };// display scheduled date @ time, recurrance selection
            Label lblOutFmt = new Label { Text = "Report format:" };
            Label lblOutName = new Label { Text = "Report name:" };
            Label lblDest = new Label { Text = "Output destination:" };
            Label lblEmail = new Label { Text = "Email to:" };
            lblAsm.ApplyStyle(normalBoldStyle);
            lblDate.ApplyStyle(normalBoldStyle);
            lblOutFmt.ApplyStyle(normalBoldStyle);
            lblOutName.ApplyStyle(normalBoldStyle);
            lblDest.ApplyStyle(normalBoldStyle);
            lblEmail.ApplyStyle(normalBoldStyle);
            // display assembly site -> name
            
            
            Label lblAsmVal = new Label();
            Label lblDateVal = new Label(); // display scheduled date @ time, recurrance selection
            Label lblOutFmtVal = new Label(); // display output format
            Label lblOutNameVal = new Label(); // display output name
            Label lblDestVal = new Label(); // display output destination site -> library
            Label lblEmailVal = new Label(); // display email name list
            lblAsmVal.ID = "lblAsmVal";
            lblAsmVal.ApplyStyle(normalStyle);
            lblDateVal.ApplyStyle(normalStyle);
            lblOutFmtVal.ApplyStyle(normalStyle);
            lblOutNameVal.ApplyStyle(normalStyle);
            lblDestVal.ApplyStyle(normalStyle);
            lblEmailVal.ApplyStyle(normalStyle);

            lblDateVal.ID = "lblDateVal";
            lblOutFmtVal.ID = "lblOutFmtVal";
            lblOutNameVal.ID = "lblOutNameVal";
            lblDestVal.ID = "lblDestVal";
            lblEmailVal.ID = "lblEmailVal";

            // label for successful run
            lblSuccess = new ASPxLabel();
            lblSuccess.ID = "lblSuccess";

            layoutTable.Rows[0].Cells[0].Controls.Add(lblHeadInfo);
            layoutTable.Rows[1].Cells[0].Controls.Add(lblAsm);
            layoutTable.Rows[2].Cells[0].Controls.Add(lblAsmVal);
            layoutTable.Rows[3].Cells[0].Controls.Add(lblDate);
            layoutTable.Rows[4].Cells[0].Controls.Add(lblDateVal);
            layoutTable.Rows[5].Cells[0].Controls.Add(lblOutFmt);
            layoutTable.Rows[6].Cells[0].Controls.Add(lblOutFmtVal);
            layoutTable.Rows[7].Cells[0].Controls.Add(lblOutName);
            layoutTable.Rows[8].Cells[0].Controls.Add(lblOutNameVal);
            layoutTable.Rows[9].Cells[0].Controls.Add(lblDest);
            layoutTable.Rows[10].Cells[0].Controls.Add(lblDestVal);
            layoutTable.Rows[13].Cells[0].Controls.Add(lblSuccess);
            layoutTable.Rows[11].Cells[0].Controls.Add(lblEmail);
            layoutTable.Rows[12].Cells[0].Controls.Add(lblEmailVal);
            reviewControl = layoutTable;
            container.Controls.Add(layoutTable);
        }

        /// <summary>
        /// Creates the review control and adds it to the container's Controls collection.
        /// In a tab-style gui, display the schedule button here.
        /// </summary>
        public static Control makeReviewControl(Control container)
        {
            //Table layoutTable = createEmptyTable(16, 1, 33, HorizontalAlign.Center);
            Table layoutTable = createEmptyTable(14, 1, 33, HorizontalAlign.Center);

            // display step header
            Label lblHeadInfo = new Label { Text = "Review details and schedule the report \n" };
            lblHeadInfo.ApplyStyle(ArrowUtils.headerStyle());
            Label lblAsm = new Label { Text = "Assembly:" };
            Label lblDate = new Label { Text = "Schedule:" };// display scheduled date @ time, recurrance selection
            Label lblOutFmt = new Label { Text = "Report format:" };
            Label lblOutName = new Label { Text = "Report name:" };
            Label lblDest = new Label { Text = "Output destination:" };
            Label lblEmail = new Label { Text = "Email to:" };
            lblAsm.ApplyStyle(normalBoldStyle);
            lblDate.ApplyStyle(normalBoldStyle);
            lblOutFmt.ApplyStyle(normalBoldStyle);
            lblOutName.ApplyStyle(normalBoldStyle);
            lblDest.ApplyStyle(normalBoldStyle);
            lblEmail.ApplyStyle(normalBoldStyle);
            // display assembly site -> name


            Label lblAsmVal = new Label();
            Label lblDateVal = new Label(); // display scheduled date @ time, recurrance selection
            Label lblOutFmtVal = new Label(); // display output format
            Label lblOutNameVal = new Label(); // display output name
            Label lblDestVal = new Label(); // display output destination site -> library
            Label lblEmailVal = new Label(); // display email name list
            lblAsmVal.ID = "lblAsmVal";
            lblAsmVal.ApplyStyle(normalStyle);
            lblDateVal.ApplyStyle(normalStyle);
            lblOutFmtVal.ApplyStyle(normalStyle);
            lblOutNameVal.ApplyStyle(normalStyle);
            lblDestVal.ApplyStyle(normalStyle);
            lblEmailVal.ApplyStyle(normalStyle);

            lblDateVal.ID = "lblDateVal";
            lblOutFmtVal.ID = "lblOutFmtVal";
            lblOutNameVal.ID = "lblOutNameVal";
            lblDestVal.ID = "lblDestVal";
            lblEmailVal.ID = "lblEmailVal";

            // label for successful run
            //lblSuccess = new ASPxLabel();
            //lblSuccess.ID = "lblSuccess";

            layoutTable.Rows[0].Cells[0].Controls.Add(lblHeadInfo);
            layoutTable.Rows[1].Cells[0].Controls.Add(lblAsm);
            layoutTable.Rows[2].Cells[0].Controls.Add(lblAsmVal);
            layoutTable.Rows[3].Cells[0].Controls.Add(lblDate);
            layoutTable.Rows[4].Cells[0].Controls.Add(lblDateVal);
            layoutTable.Rows[5].Cells[0].Controls.Add(lblOutFmt);
            layoutTable.Rows[6].Cells[0].Controls.Add(lblOutFmtVal);
            layoutTable.Rows[7].Cells[0].Controls.Add(lblOutName);
            layoutTable.Rows[8].Cells[0].Controls.Add(lblOutNameVal);
            layoutTable.Rows[9].Cells[0].Controls.Add(lblDest);
            layoutTable.Rows[10].Cells[0].Controls.Add(lblDestVal);
            //layoutTable.Rows[13].Cells[0].Controls.Add(lblSuccess);
            layoutTable.Rows[11].Cells[0].Controls.Add(lblEmail);
            layoutTable.Rows[12].Cells[0].Controls.Add(lblEmailVal);
            return layoutTable;
        }
  
        /// <summary>
        /// Generates the content of the review control.
        /// </summary>
        private void generateReviewContent()
        {
            // generate review tab content

            // assembly
            //ASPxLabel lblAsmVal = (ASPxLabel)reviewControl.FindControl("lblAsmVal");
            Label lblAsmVal = (Label)reviewControl.FindControl("lblAsmVal");
            string asmStr = null;
            if (!string.IsNullOrEmpty(templateSelector.ReportSiteGuid))
                asmStr = ArrowUtils.getFileNameFromGuid(
                     new Guid(templateSelector.ReportAssemblyGuid),
                     new Guid(templateSelector.ReportSiteGuid));
            if (asmStr == null || asmStr.LastIndexOf('/') < 0)
                asmStr = "None selected";
            else
            {
                asmStr = asmStr.Substring(asmStr.LastIndexOf('/') + 1);
                asmStr = templateSelector.RptAsmSiteTitle + " > " + asmStr;
            }
            lblAsmVal.Text = asmStr;

            // variables

            // schedule
            //ASPxLabel lblDateVal = (ASPxLabel)reviewControl.FindControl("lblDateVal");
            Label lblDateVal = (Label)reviewControl.FindControl("lblDateVal");
            try
            {
                string sDate = "";
                string sTime = "";
                sDate = scheduleSelector.StartDateTime.Date.ToLongDateString();
                sTime = scheduleSelector.StartDateTime.ToLongTimeString();
                lblDateVal.Text = "Starting " + sDate + " @ " + sTime + "\n" + getRecurranceString();

            }
            catch (FormatException fe)
            {
                lblDateVal.Text = "None selected";
            }
            //bugbug: add recurrance string; probably need a whole helper method for this

            // report name
            Label lblOutNameVal = (Label)reviewControl.FindControl("lblOutNameVal");
            if (ViewState["reportName"] != null && (string)ViewState["reportName"] != string.Empty)
                lblOutNameVal.Text = (string)ViewState["reportName"];
            else
                lblOutNameVal.Text = "None selected";

            // report format
            Label lblOutFmtVal = (Label)reviewControl.FindControl("lblOutFmtVal");
            if (ViewState["outputFormat"] != null)
                lblOutFmtVal.Text = (string)ViewState["outputFormat"];
            else
                lblOutFmtVal.Text = "None selected";

            // report destination
            Label lblDestVal = (Label)reviewControl.FindControl("lblDestVal");
            if (ViewState["outputLibGuid"] != null)
                lblDestVal.Text = outputSelector.OutLibSiteTitle + " > " + (string)ViewState["outputLibName"];
            else
                lblDestVal.Text = "None selected";

            // email to
            Label lblEmailVal = (Label)reviewControl.FindControl("lblEmailVal");
            if (ViewState["emailTo"] != null && (string)ViewState["emailTo"] != string.Empty)
                lblEmailVal.Text = (string)ViewState["emailTo"];
            else
                lblEmailVal.Text = "None selected";
        }

        /// <summary>
        /// Gets a string to display the recurrance selection to the user in human readible format.
        /// </summary>
        /// <returns></returns>
        private string getRecurranceString()
        {
            try
            {
                SchedulePeriod schedInfo = scheduleSelector.ScheduleInfo;
                return schedInfo.Description;
            }
            catch (WeeklyRecurSelException wex)
            {
                return "Invalid weekly recurrance selection.";
            }
        }

        /// <summary>
        /// Gets a string to display the recurrance selection to the user in human readible format.
        /// </summary>
        /// <returns></returns>
        public static string getRecurranceString(ScheduleSelector scheduleSelector)
        {
            try
            {
                SchedulePeriod schedInfo = scheduleSelector.ScheduleInfo;
                return schedInfo.Description;
            }
            catch (WeeklyRecurSelException wex)
            {
                return "Invalid weekly recurrance selection.";
            }
        }
        /// <summary>
        /// Creates a Table with the specified (numRows, numCols, rowHeightPx).
        /// </summary>
        /// <param name="numRows"></param>
        /// <param name="numCols"></param>
        /// <param name="rowHeightPx">height in pixels</param>
        /// <returns></returns>
        internal static Table createEmptyTable(int numRows, int numCols, int rowHeightPx, HorizontalAlign cellAlignH)
        {
            Table t = new Table();
            for (int i = 0; i < numRows; i++)
            {
                TableRow r = new TableRow();

                for (int j = 0; j < numCols; j++)
                {
                    TableCell c = new TableCell();
                    c.Height = rowHeightPx;
                    c.VerticalAlign = VerticalAlign.Top;
                    c.HorizontalAlign = cellAlignH;
                    r.Cells.Add(c);
                }
                r.Width = Unit.Percentage(100);
                t.Rows.Add(r);
            }
            return t;
        }

        /// <summary>
        /// <para>Updates the following ViewState variables:</para>
        /// <para>emailTo</para>
        /// <para>outLibSiteTitle</para>
        /// <para>outputFormat</para>
        /// <para>outputLibGuid</para>
        /// <para>outputLibName</para>
        /// <para>reportName</para>
        /// <para>rptAsmSiteTitle</para>
        /// </summary>
        private void UpdateViewStateVariables()
        {
            //ViewState["emailTo"] = scheduleSelector.EmailTo;
            ViewState["emailTo"] = outputSelector.EmailTo;
            ViewState["outLibSiteTitle"] = outputSelector.OutLibSiteTitle;
            ViewState["outputFormat"] = outputSelector.ReportFormat;
            ViewState["outputLibGuid"] = outputSelector.OutputLibraryGuid;
            ViewState["outputLibName"] = outputSelector.OutputLibraryName;
            ViewState["reportName"] = outputSelector.ReportName;
            ViewState["rptAsmSiteTitle"] = templateSelector.RptAsmSiteTitle;
        }

        private bool isValidTemplateSelection()
        {
            resetStatus();

            //bugbug: why "rptAsmInfo == null" *and* catch clause?  Originally, the code here would
            // throw an exception for an invalid report definition.  A change was made with ArrowUtils.GetReportDefFromFile,
            // and now the code will sometimes (if not always) return a null ReportDefinition instead of
            // throwing an exception.  This change was a breaking change and has been quickly patched over
            // for now by including both clauses - that might be better anyway...

            try
            {
                Guid rptDefGuid = new Guid(templateSelector.ReportAssemblyGuid);
                Guid siteGuid = new Guid(templateSelector.ReportSiteGuid);
                string errMsg = null;
                ReportDefinition rptAsmInfo = ArrowUtils.GetReportDefFromFile(siteGuid, rptDefGuid, out errMsg);
                if (rptAsmInfo == null)
                {
                    addStatusError(string.Format("The report definition is invalid or relies on a data source that is not available.\n{0}", errMsg));
                    return false;
                }
            }
            catch (Exception ex)
            {
                addStatusError(string.Format("Invalid report definition/template selection.\n{0}", ex.Message));
                return false;
            }
            resetStatus();
            return true;
        }

        private bool isValidOutputSelection()
        {
            resetStatus();

            if (String.IsNullOrEmpty(outputSelector.ReportName))
            {
                addStatusError("The report name cannot be empty.");
                return false;
            }
            if (String.IsNullOrEmpty(outputSelector.ReportFormat))
            {
                addStatusError("Invalid report format.");
                return false;
            }
            if (String.IsNullOrEmpty(outputSelector.OutputLibraryGuid))
            {
                addStatusError("Invalid report output destination.");
                return false;
            }

            resetStatus();
            return true;
        }

        /// <summary>
        /// Schedules a report using all of the specified values in the web part.
        /// </summary>
        private void scheduleReport()
        {
            try
            {
                resetStatus();

                lblSuccess.Text = "";
                lblSuccess.ForeColor = Color.FromName("Black");

                // validation
                try
                {
                    DateTime testDateTime = scheduleSelector.StartDateTime;
                }
                catch (FormatException fex)
                {
                    addStatusError("Invalid date or time.&nbsp;" + fex.Message);
                    //schedWizard.ActiveStepIndex = STEP_SCHEDULE_SELECTION; // back to select a different time
                    return;
                }

                // various inputs
                string templSiteUrl = templateSelector.ReportDefinitionSiteUrl;
                SPWeb templWeb = null;
                using (SPSite templSite = new SPSite(templSiteUrl))
                    templWeb = templSite.OpenWeb();
                DateTime startDateTime = scheduleSelector.StartDateTime;
                string reportTitle = outputSelector.ReportName;
                int reportFormat = (int)reportTypeFromString(outputSelector.ReportFormat);
                Guid outLibGuid;
                try
                {
                    outLibGuid = new Guid(outputSelector.OutputLibraryGuid);
                }
                catch (ArgumentNullException anex)
                {
                    addStatusError("INVALID OUTPUT LIBRARY SELECTION. Ensure you have not selected a website instead of a document library folder.");
                    return;
                }
                Guid outSiteGuid = new Guid(outputSelector.OutLibSiteGuid);
                SPWeb saveWeb = ArrowUtils.getSPWeb(outSiteGuid);
                SPDocumentLibrary outLib = ArrowUtils.getDocLibObject(outSiteGuid, outLibGuid);
                string outSiteUrl = saveWeb.Url;
                string outFolderUrl = string.Format("{0}/{1}", outSiteUrl, outLib.RootFolder.Url);
                if (!string.IsNullOrEmpty(outputSelector.OutputFolderGuid))
                {
                    Guid saveFolderGuid = new Guid(outputSelector.OutputFolderGuid);
                    SPFolder saveFolder = saveWeb.GetFolder(saveFolderGuid);
                    outFolderUrl = string.Format("{0}/{1}", outSiteUrl, saveFolder.Url);
                }
                //string emailTo = scheduleSelector.EmailTo;
                string emailTo = outputSelector.EmailTo;
                Guid templateGuid = scheduleSelector.tGuid;

                // get template or report assembly file name
                string templateOrRptAsmFileName;
                Guid rptAsmGuid = new Guid(templateSelector.ReportAssemblyGuid);
                templateOrRptAsmFileName = ArrowUtils.getFileNameFromGuid(rptAsmGuid, templWeb.ID);

                #region create a ScheduleItem
                ScheduleManager schedMgr = new ScheduleManager();
                //ScheduleManager.IsArrow = true;
                ScheduleItem schedItem = new ScheduleItem();
                schedItem.EmailTo = emailTo;
                schedItem.Format = (Report.REPORT_TYPE)reportFormat;
                schedItem.SaveFolder = outFolderUrl;
                schedItem.SiteUrl = templSiteUrl;
                schedItem.System = "arrow 3";
                schedItem.TemplateFileName = templateOrRptAsmFileName;
                schedItem.Title = reportTitle;
                schedItem.DataProfileGuid = string.Empty;
                schedItem.IsRptAsm = true;
                schedItem.SaveSiteUrl = ArrowUtils.getSPWeb(outSiteGuid).Url;

                // get the schedule period
                SchedulePeriod schedInfo = null;
                try
                {
                    schedInfo = scheduleSelector.ScheduleInfo;
                }
                catch (WeeklyRecurSelException wex)
                {
                    addStatusError("INVALID WEEKLY RECURRANCE SELECTION.");
                    return;
                }
                SPUser user = SPContext.Current.Web.CurrentUser;
                if (user.RegionalSettings != null)
                {
                    SPTimeZone userTimeZone = user.RegionalSettings.TimeZone;
                    DateTime utcStartDateTime = userTimeZone.LocalTimeToUTC(startDateTime);
                    DateTime serverLocalStartDateTime = utcStartDateTime.ToLocalTime();
                    schedInfo.RunTime = serverLocalStartDateTime;
                }
                else
                    schedInfo.RunTime = startDateTime;
                //schedInfo.RunZone = WindwardTimeZone.CurrentTimeZone;
                schedInfo.RunZone = TimeZoneInfo.Local;
                schedItem.Period = schedInfo;
                schedItem.CalculateRunNext();
                #endregion create a ScheduleItem

                // add the parameters
                ScheduleVarItemCollection theVars = parmSelector.getVars(templWeb.ID, rptAsmGuid);
                foreach (ScheduleVarItem var in theVars)
                    schedItem.Variables.Add(var);

                // plug schedule item into the scheduler db
                schedMgr.Insert(schedItem);

                // display success message
                lblSuccess.Text = "Report successfully scheduled.";
                lblSuccess.ForeColor = Color.FromName("Blue");

                // set start over button
                hidStartOver.Value = true.ToString();
                schedWizard.FinishCompleteButtonText = "Start Over";

                // cleanup
                templWeb.Dispose();
            }
            catch (Exception ex)
            {
                ArrowUtils.LogFatalException(log, ex);
                Controls.Clear();
                Controls.Add(new LiteralControl("<p>There was an error scheduling the report.</p>"));
            }
        }

        /// <summary>
        /// Returns the Report.REPORT_TYPE that corresponds to the specified report format string.
        /// </summary>
        /// <param name="strReportFormat"></param>
        /// <returns></returns>
        internal static Report.REPORT_TYPE reportTypeFromString(string strReportFormat)
        {
            switch (strReportFormat)
            {
                case ScheduleSelector.RPT_DOCX:
                    return Report.REPORT_TYPE.DOCX;
                case ScheduleSelector.RPT_PDF:
                    return Report.REPORT_TYPE.PDF;
                case ScheduleSelector.RPT_PPTX:
                    return Report.REPORT_TYPE.PPTX;
                case ScheduleSelector.RPT_RTF:
                    return Report.REPORT_TYPE.RTF;
                case ScheduleSelector.RPT_SML:
                    return Report.REPORT_TYPE.SPREAD_ML;
                case ScheduleSelector.RPT_WML:
                    return Report.REPORT_TYPE.WORD_ML;
                case ScheduleSelector.RPT_XLS:
                    return Report.REPORT_TYPE.XLS;
                case ScheduleSelector.RPT_XLSX:
                    return Report.REPORT_TYPE.XLSX;
                default:
                    throw new Exception("An invalid report type code was encountered");
            }
        }

        /// <summary>
        /// Resets the status label. "<h1>Schedule A Report: Ready</h1>"
        /// </summary>
        private void resetStatus()
        {
            //lblStatus.Text = "<h1>Schedule A Report: Ready</h1>";
            lblStatus.Text = string.Empty;
        }

        /// <summary>
        /// Adds the string head + msg + tail to the lblStatus
        /// </summary>
        /// <param name="head"></param>
        /// <param name="tail"></param>
        /// <param name="msg"></param>
        private void addStatusError(string msg)
        {
            lblStatus.Text += ERROR_HEAD + msg + ERROR_TAIL;
        }

        #endregion Helper & Utility Methods

    }

    interface ITemplateSelector
    {
        /// <summary>
        /// the report assembly sharepoint guid. Must be a server control
        /// property.
        /// </summary>
        string ReportAssemblyGuid { get; }
        /// <summary>
        /// The site containing the selected report assembly.  Must be a server control
        /// property.
        /// </summary>
        string RptAsmSiteTitle { get; }
        /// <summary>
        /// The url of the site containing the selected report assembly.  Must be a server 
        /// control property.
        /// </summary>
        string ReportDefinitionSiteUrl { get; }
        /// <summary>
        /// the report assembly site sharepoint guid. Must be a server control
        /// property.
        /// </summary>
        string ReportSiteGuid { get; }
        /// <summary>
        /// Indicates whether an UnauthorizedAccessException was raised when attempting to retrieve the template.
        /// </summary>
        bool IsTemplateAccessDenied { get; }
    }

    class TemplateSelector : Control, ITemplateSelector
    {
        #region Fields
        /// <summary>
        /// Header style text.
        /// </summary>
        private Style headerStyle;
        /// <summary>
        /// Normal style text.
        /// </summary>
        private Style normalStyle;
        /// <summary>
        /// The template file selector.
        /// </summary>
        private SPFileSelector templateSelector;
        #endregion Fields

        #region ITemplateSelector Members

        public string ReportAssemblyGuid
        {
            get 
            {
                // get the file info
                SPFileSelectorFileInfo templateFileInfo = templateSelector.SelectedFile;
                
                // check if a valid file is selected
                if (string.IsNullOrEmpty(templateFileInfo.Url))
                    return null;

                // return the file guid
                return templateFileInfo.Guid;
            }
        }

        public string RptAsmSiteTitle
        {
            get
            {
                // get the file info
                SPFileSelectorFileInfo templateFileInfo = templateSelector.SelectedFile;

                // check if a valid file is selected
                if (string.IsNullOrEmpty(templateFileInfo.Url))
                    return null;

                // get the file's web
                Guid webGuid = new Guid(templateFileInfo.WebGuid);
                SPWeb web = ArrowUtils.getSPWeb(webGuid);

                // return the file's web's title
                return web.Title;
            }
        }

        public string ReportDefinitionSiteUrl
        {
            get
            {
                // get the file info
                SPFileSelectorFileInfo templateFileInfo = templateSelector.SelectedFile;

                // check if a valid file is selected
                if (string.IsNullOrEmpty(templateFileInfo.Url))
                    return null;

                // get the file's web
                Guid webGuid = new Guid(templateFileInfo.WebGuid);
                SPWeb web = ArrowUtils.getSPWeb(webGuid);

                // return the file's web's url
                return web.Url;
            }
        }

        public string ReportSiteGuid
        {
            get
            {
                // get the file info
                SPFileSelectorFileInfo templateFileInfo = templateSelector.SelectedFile;

                // check if a valid file is selected
                if (string.IsNullOrEmpty(templateFileInfo.Url))
                    return null;

                // return the file's web's guid
                return templateFileInfo.WebGuid;
            }
        }

        public bool IsTemplateAccessDenied
        {
            get
            {
                return templateSelector.SelectedFile.IsAccessDenied;
            }
        }

        #endregion

        #region Constructor
        public TemplateSelector(Page parentPage)
            : base()
        {
            // set the parent page
            this.Page = parentPage;

            // set styles
            headerStyle = new Style();
            headerStyle.Font.Name = "Verdana";
            headerStyle.Font.Size = FontUnit.Parse("14pt");

            normalStyle = new Style();
            normalStyle.Font.Name = "Verdana";
            normalStyle.Font.Size = FontUnit.Parse("10pt");

            // layout table
            Table layoutTable = SchedulerWebPart.createEmptyTable(6, 2, 41, HorizontalAlign.Left);
            layoutTable.Rows[0].Cells[1].HorizontalAlign = HorizontalAlign.Center;

            // header label
            ASPxLabel headerLabel = new ASPxLabel();
            headerLabel.Text = "Step1: Pick a report definition or template file";
            headerLabel.ApplyStyle(headerStyle);
            layoutTable.Rows[1].Cells[1].Controls.Add(headerLabel);

            // select library instructions label
            ASPxLabel lblSelectLib = new ASPxLabel();
            lblSelectLib.Text = "1. Pick a document library: \n";
            lblSelectLib.ApplyStyle(normalStyle);
            layoutTable.Rows[2].Cells[1].Controls.Add(lblSelectLib);
            
            // file selector
            templateSelector = new SPFileSelector(SPFileSelector.Mode.File);
            layoutTable.Rows[3].Cells[1].Controls.Add(templateSelector);

            // add the layout table
            Controls.Add(layoutTable);
        }
        #endregion Constructor
    }

    interface IOutputSelector
    {
        /// <summary>
        /// The output report's name (minus extention)
        /// </summary>
        string ReportName { get; }
        /// <summary>
        /// The selected report format
        /// </summary>
        string ReportFormat { get; }
        /// <summary>
        /// The SharePoint guid of the destination document library
        /// </summary>
        string OutputLibraryGuid { get; }
        /// <summary>
        /// The name of the destination document library
        /// </summary>
        string OutputLibraryName { get; }
        /// <summary>
        /// The site containing the selected report assembly
        /// </summary>
        string OutLibSiteTitle { get; }
        /// <summary>
        /// The site containing the selected report assembly
        /// </summary>
        string OutLibSiteGuid { get; }
        /// <summary>
        /// The output folder guid
        /// </summary>
        string OutputFolderGuid { get; }
        /// <summary>
        /// The email list
        /// </summary>
        string EmailTo { get; }
    }

    class OutputSelector : Control, IOutputSelector
    {
        #region Controls
        
        /// <summary>
        /// Label for output document name
        /// </summary>
        private ASPxLabel lblOutName;
        /// <summary>
        /// Output document name text box
        /// </summary>
        private TextBox txtOutName;
        /// <summary>
        /// Output format label
        /// </summary>
        private ASPxLabel lblOutFormat;
        /// <summary>
        /// Output format list
        /// </summary>
        private DropDownList lstOutFormat;
        /// <summary>
        /// Header style text.
        /// </summary>
        private Style headerStyle;
        /// <summary>
        /// Normal style text.
        /// </summary>
        private Style normalStyle;
        /// <summary>
        /// Normal size, bold text.
        /// </summary>
        private Style normalBoldStyle;
        /// <summary>
        /// the output folder selector
        /// </summary>
        private SPFileSelector outputFolderSelector;
        /// <summary>
        /// email recipients label
        /// </summary>
        private ASPxLabel lblEmailTo;
        /// <summary>
        /// email recipients text box
        /// </summary>
        private TextBox txtEmailTo;
        
        #endregion Controls

        #region Fields

        private const string RPT_DOCX = "DOCX";
        private const string RPT_SML = "SML";
        private const string RPT_PDF = "PDF";
        private const string RPT_PPTX = "PPTX";
        private const string RPT_RTF = "RTF";
        private const string RPT_WML = "WML";
        private const string RPT_XLS = "XLS";
        private const string RPT_XLSX = "XLSX";
        /// <summary>
        /// the log
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(OutputSelector));

        #endregion Fields

        #region IOutputSelector

        /// <summary>
        /// The output report's name (minus extention)
        /// </summary>
        public string ReportName { get { return txtOutName.Text.Trim(); } }
        /// <summary>
        /// The selected report format
        /// </summary>
        public string ReportFormat { get { return lstOutFormat.SelectedValue; } }
        /// <summary>
        /// The SharePoint guid of the destination document library
        /// </summary>
        public string OutputLibraryGuid
        {
            get
            {
                // get the selected folder info
                SPFileSelectorFolderInfo outFolderInfo = outputFolderSelector.SelectedFolder;

                // check if the selected folder is valid
                if (!outFolderInfo.IsSPFolder)
                    return null;

                // return the output folder's doc lib's guid
                return outFolderInfo.LibGuid;
            }
        }
        /// <summary>
        /// The name of the destination document library
        /// </summary>
        public string OutputLibraryName
        {
            get
            {
                // get the selected folder info
                SPFileSelectorFolderInfo outFolderInfo = outputFolderSelector.SelectedFolder;

                // check if the selected folder is valid
                if (!outFolderInfo.IsSPFolder)
                    return null;

                // return the output folder's lib's name
                Guid webGuid = new Guid(outFolderInfo.WebGuid);
                Guid libGuid = new Guid(outFolderInfo.LibGuid);
                SPDocumentLibrary lib = ArrowUtils.getDocLibObject(webGuid, libGuid);
                return lib.Title;
            }
        }
        /// <summary>
        /// The site containing the selected report assembly
        /// </summary>
        public string OutLibSiteTitle
        {
            get
            {
                // get the selected folder info
                SPFileSelectorFolderInfo outFolderInfo = outputFolderSelector.SelectedFolder;

                // check if the selected folder is valid
                if (!outFolderInfo.IsSPFolder)
                    return null;

                // return the output folder's site's name
                Guid webGuid = new Guid(outFolderInfo.WebGuid);
                SPWeb web = ArrowUtils.getSPWeb(webGuid);
                return web.Title;
            }
        }
        /// <summary>
        /// The site containing the selected report assembly
        /// </summary>
        public string OutLibSiteGuid
        {
            get
            {
                // get the selected folder info
                SPFileSelectorFolderInfo outFolderInfo = outputFolderSelector.SelectedFolder;

                // check if the selected folder is valid
                if (!outFolderInfo.IsSPFolder)
                    return null;

                // return the output folder's site's guid
                return outFolderInfo.WebGuid;
            }
        }
        /// <summary>
        /// The output folder guid.
        /// </summary>
        public string OutputFolderGuid
        {
            get
            {
                // get the selected folder info
                SPFileSelectorFolderInfo outFolderInfo = outputFolderSelector.SelectedFolder;

                // check if the selected folder is valid
                if (!outFolderInfo.IsSPFolder)
                    return null;

                // return the output folder's guid
                return outFolderInfo.Guid;
            }
        }
        /// <summary>
        /// The email list
        /// </summary>
        public string EmailTo { get { return txtEmailTo.Text == null ? string.Empty : txtEmailTo.Text.Trim(); } }

        #endregion IOutputSelector

        #region Constructor
        public OutputSelector()
            : base()
        {
            if (log.IsDebugEnabled)
                log.Debug("Creating output selector");

            // set styles
            headerStyle = new Style();
            headerStyle.Font.Name = "Verdana";
            headerStyle.Font.Size = FontUnit.Parse("14pt");

            normalStyle = new Style();
            normalStyle.Font.Name = "Verdana";
            normalStyle.Font.Size = FontUnit.Parse("10pt");

            normalBoldStyle = new Style();
            normalBoldStyle.Font.Name = "Verdana";
            normalBoldStyle.Font.Size = FontUnit.Parse("12pt");
            normalBoldStyle.Font.Bold = true;

            Style normalSmallStyle = new Style();
            normalSmallStyle.Font.Name = "Verdana";
            normalSmallStyle.Font.Size = FontUnit.Parse("8pt");


            // load the site collection
            SPSite siteCollection = SPContext.Current.Site;

            // load the site, we need it for the lists
            SPWeb site = SPContext.Current.Web;

            // layout table
            Table layoutTable = SchedulerWebPart.createEmptyTable(5, 2, 41, HorizontalAlign.Left);
            layoutTable.Rows[0].Cells[1].HorizontalAlign = HorizontalAlign.Center;

            // header label
            ASPxLabel headerLabel = new ASPxLabel();
            headerLabel.Text = "Step 4: Pick a document library to save the report in.";
            headerLabel.ApplyStyle(headerStyle);
            layoutTable.Rows[1].Cells[1].Controls.Add(headerLabel);

            // select library instructions label
            ASPxLabel lblSelectLib = new ASPxLabel();
            lblSelectLib.Text = "1. Pick a document library folder: \n";
            lblSelectLib.ApplyStyle(normalStyle);
            layoutTable.Rows[2].Cells[1].Controls.Add(lblSelectLib);

            // output folder selector
            outputFolderSelector = new SPFileSelector(SPFileSelector.Mode.Folder);
            layoutTable.Rows[3].Cells[1].Controls.Add(outputFolderSelector);

            Controls.Add(layoutTable);

            // output document name label
            string tdStyle = "padding:15px;";
            Controls.Add(new LiteralControl("<table><tr><td style=\"" + tdStyle + "\">"));
            lblOutName = new ASPxLabel();
            lblOutName.Text = "Report name:";
            lblOutName.ApplyStyle(normalStyle);
            this.Controls.Add(lblOutName);

            // output document name text box
            Controls.Add(new LiteralControl("</td><td style=\"" + tdStyle + "\">"));
            txtOutName = new TextBox();
            this.Controls.Add(txtOutName);

            // output format label
            Controls.Add(new LiteralControl("</td></tr><tr><td style=\"" + tdStyle + "\">"));
            lblOutFormat = new ASPxLabel();
            lblOutFormat.Text = "Report format:";
            lblOutFormat.ApplyStyle(normalStyle);
            this.Controls.Add(lblOutFormat);

            // output format drop down list
            Controls.Add(new LiteralControl("</td><td style=\"" + tdStyle + "\">"));
            lstOutFormat = new DropDownList();
            lstOutFormat.Items.Add(new ListItem("DOCX", RPT_DOCX));
            lstOutFormat.Items.Add(new ListItem("Excel 2003 XML", RPT_SML));
            lstOutFormat.Items.Add(new ListItem("PDF", RPT_PDF));
            lstOutFormat.Items.Add(new ListItem("PPTX", RPT_PPTX));
            lstOutFormat.Items.Add(new ListItem("RTF", RPT_RTF));
            lstOutFormat.Items.Add(new ListItem("Word 2003 XML", RPT_WML));
            lstOutFormat.Items.Add(new ListItem("XLS", RPT_XLS));
            lstOutFormat.Items.Add(new ListItem("XLSX", RPT_XLSX));
            this.Controls.Add(lstOutFormat);

            // email to label
            Controls.Add(new LiteralControl("</td></tr><tr><td style=\"" + tdStyle + "\">"));
            lblEmailTo = new ASPxLabel();
            lblEmailTo.Text = "Email to:\n";
            lblEmailTo.ApplyStyle(normalStyle);
            Controls.Add(lblEmailTo);
            ASPxLabel lblEmailToSub = new ASPxLabel();
            lblEmailToSub.Text = "(alpha@abc.com;\nbeta@cde.com;...)";
            lblEmailToSub.ApplyStyle(normalSmallStyle);
            Controls.Add(lblEmailToSub);

            // email to text box
            Controls.Add(new LiteralControl("</td><td style=\"" + tdStyle + "\" valign=\"top\">"));
            txtEmailTo = new TextBox();
            Controls.Add(txtEmailTo);
            Controls.Add(new LiteralControl("</td></tr></table>"));
        }
        #endregion Constructor

        #region Explicit Interface Overrides



        #endregion Explicit Interface Overrides

        #region Event Handlers



        #endregion Event Handlers

        #region Helper & Utility Methods



        #endregion Helper & Utility Methods
    }


    public class ScheduleSelector : Control
    {
        /// <summary>
        /// title and status
        /// </summary>
        private Label lblStatus;
        /// <summary>
        /// Start date label
        /// </summary>
        private Label lblStartDate;
        /// <summary>
        /// Calendar selector for start date
        /// </summary>
        private WC.Calendar calStartDate;
        /// <summary>
        /// start time label
        /// </summary>
        private Label lblStartTime;
        ///// <summary>
        ///// sql time
        ///// </summary>
        //private TextBox txtStartTime;
        /// <summary>
        /// the hour to start on
        /// </summary>
        private DropDownList ddlStartHr;
        /// <summary>
        /// The minute to start on
        /// </summary>
        private DropDownList ddlStartMin;
        /// <summary>
        /// Start on am or pm
        /// </summary>
        private DropDownList ddlStartAmPm;
        /// <summary>
        /// list of recurrance types
        /// </summary>
        private RadioButtonList rblRecurType;
        /// <summary>
        /// defines a daily scheduling period
        /// </summary>
        private DailyRecurSel dailyRecurSel;
        /// <summary>
        /// defines a weekly scheduling period
        /// </summary>
        private WeeklyRecurSel weeklyRecurSel;
        /// <summary>
        /// defines a monthly scheduling period
        /// </summary>
        private MonthlyRecurSel monthlyRecurSel;
        /// <summary>
        /// defines a yearly scheduling period
        /// </summary>
        private YearlyRecurSel yearlyRecurSel;
        /// <summary>
        /// Gets the selected start date and time
        /// </summary>
        public DateTime StartDateTime 
        { 
            get 
            {
                //DateTime selectedUserLocal = calStartDate.SelectedDate.Add(DateTime.Parse(txtStartTime.Text.Trim()).TimeOfDay);
                DateTime selectedUserLocal = calStartDate.SelectedDate;
                selectedUserLocal = selectedUserLocal.AddMinutes(Int32.Parse(ddlStartMin.SelectedValue));
                if (ddlStartHr.SelectedValue != "12")
                    selectedUserLocal = selectedUserLocal.AddHours(Int32.Parse(ddlStartHr.SelectedValue));
                if (ddlStartAmPm.SelectedValue.ToUpper().Equals("PM"))
                    selectedUserLocal = selectedUserLocal.AddHours(12);
                return selectedUserLocal;
            } 
        }
        /// <summary>
        /// Gets the recurrance type "daily", or "monthly"
        /// </summary>
        public string RecurranceType { get { return this.rblRecurType.SelectedValue.ToLower(); } }
        /// <summary>
        /// The schedule period info
        /// </summary>
        public SchedulePeriod ScheduleInfo
        {
            get
            {
                switch (RecurranceType)
                {
                    case "daily":
                        return dailyRecurSel.ScheduleInfo;
                    case "monthly":
                        return monthlyRecurSel.ScheduleInfo;
                    case "weekly":
                        return weeklyRecurSel.ScheduleInfo;
                    case "yearly":
                        return yearlyRecurSel.ScheduleInfo;
                    default:
                        throw new Exception("An invalid recurrance type was encountered");
                }
            }
        }
        /// <summary>
        /// the report title (minus format extention)
        /// </summary>
        public string outName;
        /// <summary>
        /// the template's sharepoint guid
        /// </summary>
        public Guid tGuid;
        /// <summary>
        /// the report format
        /// </summary>
        public string outFmt;
        /// <summary>
        /// the report's document library sharepoint guid
        /// </summary>
        public Guid outLibGuid;

        private Style headerStyle;
        private Style normalStyle;
        private Style normalBoldStyle;
        
        public const string RPT_DOCX = "DOCX";
        public const string RPT_SML = "SML";
        public const string RPT_PDF = "PDF";
        public const string RPT_PPTX = "PPTX";
        public const string RPT_RTF = "RTF";
        public const string RPT_WML = "WML";
        public const string RPT_XLS = "XLS";
        public const string RPT_XLSX = "XLSX";

        private static ILog log = LogManager.GetLogger(typeof(ScheduleSelector));

        /// <summary>
        /// Creates a ScheduleSelector.  The template guid, report title,
        /// output format, and output library guid will have to be specified
        /// separately.
        /// </summary>
        public ScheduleSelector()
            : base()
        {
            if (log.IsDebugEnabled)
                log.Debug("creating schedule selector");
            ctor();
        }

        /// <summary>
        /// Creates/initializes the controls.  Does not require the template guid,
        /// report title, output format, or output library guid to be set.  However,
        /// scheduling before setting these will cause an error.
        /// </summary>
        private void ctor()
        {
            // status label
            lblStatus = new Label();
            resetStatus();
            Controls.Add(lblStatus);

            // layout table
            Table layoutTable = SchedulerWebPart.createEmptyTable(2, 2, 41, HorizontalAlign.Left);
            layoutTable.Rows[0].Cells[1].HorizontalAlign = HorizontalAlign.Center;

            // header label
            ASPxLabel headerLabel = new ASPxLabel();
            headerLabel.Text = "Select the schedule settings.";
            headerLabel.ApplyStyle(ArrowUtils.headerStyle());
            layoutTable.Rows[0].Cells[1].Controls.Add(headerLabel);

            // add header labels
            Controls.Add(layoutTable);

            // time zone
            Label lblTimeZone = new Label();
            string timeZone = string.Empty;
            SPRegionalSettings userRegion = SPContext.Current.Web.CurrentUser.RegionalSettings;
            if (userRegion != null)
                timeZone = userRegion.TimeZone.Description;
            else
                timeZone = TimeZone.CurrentTimeZone.StandardName;
            lblTimeZone.Text = timeZone;
            Controls.Add(lblTimeZone);

            // calendar, time, email table
            Controls.Add(new LiteralControl("<table><tr><td>"));
            // start date label
            lblStartDate = new Label();
            lblStartDate.Text = "<br/>Start date:&nbsp;";
            Controls.Add(lblStartDate);
            // start date calendar selector
            calStartDate = new WC.Calendar();
            calStartDate.SelectedDate = DateTime.Today;
            Controls.Add(calStartDate);
            Controls.Add(new LiteralControl("</td><td>"));
            // start time label
            lblStartTime = new Label();
            lblStartTime.Text = "<br/>Start time:<br/>";
            Controls.Add(lblStartTime);
            // start time text box
            //txtStartTime = new TextBox();
            //Controls.Add(txtStartTime);
            ddlStartHr = new DropDownList();
            ddlStartHr.Items.Add("1");
            ddlStartHr.Items.Add("2");
            ddlStartHr.Items.Add("3");
            ddlStartHr.Items.Add("4");
            ddlStartHr.Items.Add("5");
            ddlStartHr.Items.Add("6");
            ddlStartHr.Items.Add("7");
            ddlStartHr.Items.Add("8");
            ddlStartHr.Items.Add("9");
            ddlStartHr.Items.Add("10");
            ddlStartHr.Items.Add("11");
            ddlStartHr.Items.Add("12");
            Controls.Add(ddlStartHr);
            Controls.Add(new LiteralControl("&nbsp;:&nbsp;"));
            ddlStartMin = new DropDownList();
            ddlStartMin.Items.Add("00");
            ddlStartMin.Items.Add("05");
            ddlStartMin.Items.Add("10");
            ddlStartMin.Items.Add("15");
            ddlStartMin.Items.Add("20");
            ddlStartMin.Items.Add("25");
            ddlStartMin.Items.Add("30");
            ddlStartMin.Items.Add("35");
            ddlStartMin.Items.Add("40");
            ddlStartMin.Items.Add("45");
            ddlStartMin.Items.Add("50");
            ddlStartMin.Items.Add("55");
            Controls.Add(ddlStartMin);
            Controls.Add(new LiteralControl("&nbsp;&nbsp;"));
            ddlStartAmPm = new DropDownList();
            ddlStartAmPm.Items.Add("AM");
            ddlStartAmPm.Items.Add("PM");
            Controls.Add(ddlStartAmPm);
            Controls.Add(new LiteralControl("</td></tr></table>"));

            // recurrence selector
            Controls.Add(new LiteralControl("<br/><br/>"));
            Panel pnlRecurSel = new Panel();
            //pnlRecurSel.BorderStyle = BorderStyle.Solid;
            //pnlRecurSel.BorderWidth = new Unit(1);
            //pnlRecurSel.BorderColor = System.Drawing.Color.Silver;
            Controls.Add(pnlRecurSel);

            // label for the selector
            pnlRecurSel.Controls.Add(new LiteralControl("Recurrance"));

            pnlRecurSel.Controls.Add(new LiteralControl("<table><tr><td>"));
            rblRecurType = new RadioButtonList();
            rblRecurType.Items.Add("Daily");
            rblRecurType.Items.Add("Weekly");
            rblRecurType.Items.Add("Monthly");
            rblRecurType.Items.Add("Yearly");
            rblRecurType.SelectedIndexChanged += new EventHandler(rblRecurType_SelectedIndexChanged);
            rblRecurType.AutoPostBack = true;
            rblRecurType.SelectedIndex = 1;
            pnlRecurSel.Controls.Add(rblRecurType);
            pnlRecurSel.Controls.Add(new LiteralControl("</td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td><td>"));

            dailyRecurSel = new DailyRecurSel();
            dailyRecurSel.Visible = false;
            pnlRecurSel.Controls.Add(dailyRecurSel);

            weeklyRecurSel = new WeeklyRecurSel();
            weeklyRecurSel.Visible = true;
            pnlRecurSel.Controls.Add(weeklyRecurSel);

            monthlyRecurSel = new MonthlyRecurSel();
            monthlyRecurSel.Visible = false;
            pnlRecurSel.Controls.Add(monthlyRecurSel);

            yearlyRecurSel = new YearlyRecurSel();
            yearlyRecurSel.Visible = false;
            pnlRecurSel.Controls.Add(yearlyRecurSel);

            pnlRecurSel.Controls.Add(new LiteralControl("</td></tr></table>"));
        }

        void rblRecurType_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling recursion type changed");

            // bugbug: test devexpress controls
            RadioButtonList rblRecurType = sender as RadioButtonList;
            if (rblRecurType == null)
                throw new Exception("rblRecurType was null in event handler");
            switch (rblRecurType.SelectedValue)
            {
                case "Daily":
                    dailyRecurSel.Visible = true;
                    weeklyRecurSel.Visible = false;
                    monthlyRecurSel.Visible = false;
                    yearlyRecurSel.Visible = false;
                    break;
                case "Weekly":
                    dailyRecurSel.Visible = false;
                    weeklyRecurSel.Visible = true;
                    monthlyRecurSel.Visible = false;
                    yearlyRecurSel.Visible = false;
                    break;
                case "Monthly":
                    dailyRecurSel.Visible = false;
                    weeklyRecurSel.Visible = false;
                    monthlyRecurSel.Visible = true;
                    yearlyRecurSel.Visible = false;
                    break;
                case "Yearly":
                    dailyRecurSel.Visible = false;
                    weeklyRecurSel.Visible = false;
                    monthlyRecurSel.Visible = false;
                    yearlyRecurSel.Visible = true;
                    break;
                default:
                    Exception ex;
                    if (rblRecurType.SelectedValue == null)
                        ex =  new Exception("The rblRecurType's selected value was null");
                    else 
                        ex = new Exception("The rblRecurType's selected value was not Daily or Monthly");
                    ArrowUtils.LogFatalException(log, ex);
                    throw ex;
            }
        }

        /// <summary>
        /// Resets the status label to be empty
        /// </summary>
        private void resetStatus()
        {
            lblStatus.Text = string.Empty;
        }

        /// <summary>
        /// Adds the string head + msg + tail to the lblStatus
        /// </summary>
        /// <param name="head"></param>
        /// <param name="tail"></param>
        /// <param name="msg"></param>
        private void addStatusError(string head, string msg, string tail)
        {
            lblStatus.Text += head + msg + tail;
        }
    }

    class DailyRecurSel : Control
    {
        /// <summary>
        /// selects the every x days option
        /// </summary>
        private RadioButton rbEveryXDays;
        /// <summary>
        /// the number of days between running
        /// </summary>
        //private ASPxSpinEdit spinNumDays;
        private TextBox spinNumDays;
        /// <summary>
        /// selects the every weekday option
        /// </summary>
        private RadioButton rbEveryWeekday;
        /// <summary>
        /// contains the run cycle and the period
        /// </summary>
        public ScheduleDaily ScheduleInfo
        {
            get
            {
                ScheduleDaily schedInfo;

                if (rbEveryXDays.Checked)
                {
                    //schedInfo = new ScheduleDaily(ScheduleDaily.CYCLE.PERIOD, (int)spinNumDays.Number);
                    schedInfo = new ScheduleDaily(ScheduleDaily.CYCLE.PERIOD, int.Parse(spinNumDays.Text.Trim()));
                }
                else
                {
                    schedInfo = new ScheduleDaily(ScheduleDaily.CYCLE.WEEKDAYS, int.Parse(spinNumDays.Text.Trim()));
                }

                return schedInfo;
            }
        }

        private static ILog log = LogManager.GetLogger(typeof(DailyRecurSel));

        public DailyRecurSel()
            : base()
        {
            if (log.IsDebugEnabled)
                log.Debug("creating daily recursion selector");

            // title
            //Controls.Add(new LiteralControl("<br/><br/>Recur Daily<br/>"));

            // radio buttons
            Controls.Add(new LiteralControl("<table>"));

            // every x days option
            Controls.Add(new LiteralControl("<tr><td>"));
            rbEveryXDays = new RadioButton();
            rbEveryXDays.Checked = true;
            rbEveryXDays.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
            rbEveryXDays.AutoPostBack = true;
            Controls.Add(rbEveryXDays);
            Controls.Add(new LiteralControl("</td><td>Every</td><td>"));
            //spinNumDays = new ASPxSpinEdit();
            spinNumDays = new TextBox();
            //spinNumDays.MinValue = 1;
            //spinNumDays.MaxValue = 999;
            //spinNumDays.NumberType = SpinEditNumberType.Integer;
            //spinNumDays.MaxLength = 3;
            //spinNumDays.Value = 1;
            spinNumDays.Text = "1";
            spinNumDays.Width = new Unit(.5, UnitType.Inch);
            Controls.Add(spinNumDays);
            Controls.Add(new LiteralControl("</td><td>days</td></tr>"));

            // every weekday option
            Controls.Add(new LiteralControl("<tr><td>"));
            rbEveryWeekday = new RadioButton();
            rbEveryWeekday.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
            rbEveryWeekday.AutoPostBack = true;
            Controls.Add(rbEveryWeekday);
            Controls.Add(new LiteralControl("</td><td colspan=\"3\">Every weekday</td></tr>"));

            // end of the radio buttons
            Controls.Add(new LiteralControl("</table>"));
        }

        void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling radio checked changed");

            RadioButton rb = sender as RadioButton;
            if (rb == rbEveryWeekday)
            {
                rbEveryXDays.Checked = false;
            }
            else if (rb == rbEveryXDays)
            {
                rbEveryWeekday.Checked = false;
            }
        }
    }

    class MonthlyRecurSel : Control
    {
        /// <summary>
        /// Day-based option. Indicates schedule for every Xth day of every Yth month
        /// </summary>
        private RadioButton rbDayBased;
        /// <summary>
        /// the xth day in the day-based option
        /// </summary>
        //private ASPxSpinEdit spinDayNum;
        private TextBox spinDayNum;
        /// <summary>
        /// The Yth month in the day-based option
        /// </summary>
        //private ASPxSpinEdit spinMonthDayBased;
        private TextBox spinMonthDayBased;

        /// <summary>
        /// Week-based option. Indicates schedule for every Xth week of every Yth month
        /// </summary>
        private RadioButton rbWeekBased;
        /// <summary>
        /// ScheduleMonth.WEEK list
        /// </summary>
        private DropDownList ddlWeekdayIndex;
        /// <summary>
        /// ScheduleMonth.WEEKDAY list
        /// </summary>
        private DropDownList ddlWeekdays;
        /// <summary>
        /// The Yth month in the week-based option
        /// </summary>
        //private ASPxSpinEdit spinMonthWeekBased;
        private TextBox spinMonthWeekBased;
        /// <summary>
        /// the log
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(MonthlyRecurSel));

        /// <summary>
        /// Gets the monthly recurrance info
        /// </summary>
        public ScheduleMonthly ScheduleInfo
        {
            get
            {
                ScheduleMonthly monthlyInfo;

                if (rbDayBased.Checked)
                {
                    //monthlyInfo = new ScheduleMonthly((int)spinDayNum.Number, (int)spinMonthDayBased.Number);
                    monthlyInfo = new ScheduleMonthly(int.Parse(spinDayNum.Text.Trim()), int.Parse(spinMonthDayBased.Text.Trim()));
                    return monthlyInfo;
                }
                else
                {
                    ScheduleMonth.WEEK week = scheduleWeekFromString(ddlWeekdayIndex.SelectedValue);
                    ScheduleMonth.WEEKDAY weekday = scheduleWeekdayfromString(ddlWeekdays.SelectedValue);
                    //monthlyInfo = new ScheduleMonthly(week, weekday, (int)spinMonthWeekBased.Number);
                    monthlyInfo = new ScheduleMonthly(week, weekday, int.Parse(spinMonthWeekBased.Text.Trim()));
                    return monthlyInfo;
                }
            }
        }

        public MonthlyRecurSel()
            : base()
        {
            if (log.IsDebugEnabled)
                log.Debug("Creating monthly recursion selector");

            // title
            //Controls.Add(new LiteralControl("<br/><br/>Recur Monthly<br/>"));

            // day-based option
            Controls.Add(new LiteralControl("<table><tr><td>"));
            // radio button
            rbDayBased = new RadioButton();
            rbDayBased.Checked = true;
            rbDayBased.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
            rbDayBased.AutoPostBack = true;
            Controls.Add(rbDayBased);
            // day label
            Controls.Add(new LiteralControl("</td><td>Day</td><td>"));
            // day num
            //spinDayNum = new ASPxSpinEdit();
            spinDayNum = new TextBox();
            //spinDayNum.MinValue = 1;
            //spinDayNum.MaxValue = 999;
            //spinDayNum.NumberType = SpinEditNumberType.Integer;
            //spinDayNum.MaxLength = 3;
            //spinDayNum.Value = 1;
            spinDayNum.Text = "1";
            spinDayNum.Width = new Unit(.5, UnitType.Inch);
            Controls.Add(spinDayNum);
            // month label
            Controls.Add(new LiteralControl("</td><td>of every</td><td>"));
            // month num
            //spinMonthDayBased = new ASPxSpinEdit();
            spinMonthDayBased = new TextBox();
            //spinMonthDayBased.MinValue = 1;
            //spinMonthDayBased.MaxValue = 999;
            //spinMonthDayBased.NumberType = SpinEditNumberType.Integer;
            //spinMonthDayBased.MaxLength = 3;
            //spinMonthDayBased.Value = 1;
            spinMonthDayBased.Text = "1";
            spinMonthDayBased.Width = new Unit(.5, UnitType.Inch);
            Controls.Add(spinMonthDayBased);
            // end label
            Controls.Add(new LiteralControl("</td><td>month(s)</td></tr></table>"));

            // week-based option
            Controls.Add(new LiteralControl("<table><tr><td>"));
            // radio button
            rbWeekBased = new RadioButton();
            rbWeekBased.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
            rbWeekBased.AutoPostBack = true;
            Controls.Add(rbWeekBased);
            Controls.Add(new LiteralControl("</td><td>"));
            // list of weekday indecies
            ddlWeekdayIndex = new DropDownList();
            ddlWeekdayIndex.Items.Add(new ListItem("first", "first"));
            ddlWeekdayIndex.Items.Add(new ListItem("second", "second"));
            ddlWeekdayIndex.Items.Add(new ListItem("third", "third"));
            ddlWeekdayIndex.Items.Add(new ListItem("fourth", "fourth"));
            ddlWeekdayIndex.Items.Add(new ListItem("last", "last"));
            Controls.Add(ddlWeekdayIndex);
            Controls.Add(new LiteralControl("</td><td>"));
            // list of weekdays
            ddlWeekdays = new DropDownList();
            ddlWeekdays.Items.Add(new ListItem("day", "first_day"));
            ddlWeekdays.Items.Add(new ListItem("weekday", "first_weekday"));
            ddlWeekdays.Items.Add(new ListItem("weekend day", "first_weekend"));
            ddlWeekdays.Items.Add(new ListItem("Sunday", "sunday"));
            ddlWeekdays.Items.Add(new ListItem("Monday", "monday"));
            ddlWeekdays.Items.Add(new ListItem("Tuesday", "tuesday"));
            ddlWeekdays.Items.Add(new ListItem("Wednesday", "wednesday"));
            ddlWeekdays.Items.Add(new ListItem("Thursday", "thursday"));
            ddlWeekdays.Items.Add(new ListItem("Friday", "friday"));
            ddlWeekdays.Items.Add(new ListItem("Saturday", "saturday"));
            Controls.Add(ddlWeekdays);
            // month period label
            Controls.Add(new LiteralControl("</td><td>of every</td><td>"));
            // month spinner
            //spinMonthWeekBased = new ASPxSpinEdit();
            spinMonthWeekBased = new TextBox();
            //spinMonthWeekBased.MinValue = 1;
            //spinMonthWeekBased.MaxValue = 999;
            //spinMonthWeekBased.NumberType = SpinEditNumberType.Integer;
            //spinMonthWeekBased.MaxLength = 3;
            //spinMonthWeekBased.Value = 1;
            spinMonthWeekBased.Text = "1";
            spinMonthWeekBased.Width = new Unit(.5, UnitType.Inch);
            Controls.Add(spinMonthWeekBased);
            // end label
            Controls.Add(new LiteralControl("</td><td>month</td></tr></table>"));
        }

        void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling radio checked changed");

            RadioButton rb = sender as RadioButton;
            if (rb == rbDayBased)
            {
                rbWeekBased.Checked = false;
            }
            else if (rb == rbWeekBased)
            {
                rbDayBased.Checked = false;
            }
        }

        
        public static ScheduleMonth.WEEK scheduleWeekFromString(string strWeek)
        {
            switch (strWeek)
            {
                case "first":
                    return ScheduleMonth.WEEK.FIRST;
                case "second":
                    return ScheduleMonth.WEEK.SECOND;
                case "third":
                    return ScheduleMonth.WEEK.THIRD;
                case "fourth":
                    return ScheduleMonth.WEEK.FOURTH;
                case "last":
                    return ScheduleMonth.WEEK.LAST;
                default:
                    Exception ex = new Exception("An internal error in the monthly schedule recurrence control caused an invalid week to be selected.");
                    ArrowUtils.LogFatalException(log, ex);
                    throw ex;
            }
        }


        public static ScheduleMonth.WEEKDAY scheduleWeekdayfromString(string strWeekday)
        {
            switch (strWeekday)
            {
                case "first_day":
                    return ScheduleMonth.WEEKDAY.FIRST_DAY;
                case "first_weekday":
                    return ScheduleMonth.WEEKDAY.FIRST_WEEKDAY;
                case "first_weekend":
                    return ScheduleMonth.WEEKDAY.FIRST_WEEKEND;
                case "sunday":
                    return ScheduleMonth.WEEKDAY.SUNDAY;
                case "monday":
                    return ScheduleMonth.WEEKDAY.MONDAY;
                case "tuesday":
                    return ScheduleMonth.WEEKDAY.TUESDAY;
                case "wednesday":
                    return ScheduleMonth.WEEKDAY.WEDNESDAY;
                case "thursday":
                    return ScheduleMonth.WEEKDAY.THURSDAY;
                case "friday":
                    return ScheduleMonth.WEEKDAY.FRIDAY;
                case "saturday":
                    return ScheduleMonth.WEEKDAY.SATURDAY;
                default:
                    Exception ex = new Exception("An internal error in the monthly schedule recurrence control caused an invalid weekday to be selected.");
                    ArrowUtils.LogFatalException(log, ex);
                    throw ex;
            }
        }
    }

    class WeeklyRecurSelException : Exception
    {
        public WeeklyRecurSelException(string message) : base(message) { }

        public WeeklyRecurSelException(string message, Exception innerException) 
            : base(message, innerException)
        {
        }
    }

    class WeeklyRecurSel : Control
    {
        /// <summary>
        /// period of number of weeks (eg, every 2nd week, 3rd, ...)
        /// </summary>
        //private ASPxSpinEdit spinNumWeeks;
        private TextBox spinNumWeeks;
        /// <summary>
        /// the day of the week, like Sunday, Monday, ...
        /// </summary>
        private CheckBoxList chklDayOfWeek;
        /// <summary>
        /// the log
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(WeeklyRecurSel));

        public ScheduleWeekly ScheduleInfo
        {
            get
            {
                //ScheduleWeekly weeklyInfo = new ScheduleWeekly((int)spinNumWeeks.Number, getDays());
                int numSelected = 0;
                foreach (ListItem item in chklDayOfWeek.Items)
                {
                    if (item.Selected)
                        numSelected++;
                }
                if (numSelected < 1)
                    throw new WeeklyRecurSelException("Invalid weekly selection.  At least one day of week must be selected.");


                ScheduleWeekly weeklyInfo = new ScheduleWeekly(int.Parse(spinNumWeeks.Text.Trim()), getDays());
                return weeklyInfo;
            }
        }

        public WeeklyRecurSel()
            : base()
        {
            if (log.IsDebugEnabled)
                log.Debug("Creating weekly recursion selector");

            // title
            //Controls.Add(new LiteralControl("<br/><br/>Recur Weekly<br/>"));

            // weekly period row
            Controls.Add(new LiteralControl("<table><tr><td>Every</td><td>"));
            // weekly period spinner
            //spinNumWeeks = new ASPxSpinEdit();
            spinNumWeeks = new TextBox();
            //spinNumWeeks.MinValue = 1;
            //spinNumWeeks.MaxValue = 999;
            //spinNumWeeks.NumberType = SpinEditNumberType.Integer;
            //spinNumWeeks.MaxLength = 3;
            //spinNumWeeks.Value = 1;
            spinNumWeeks.Text = "1";
            spinNumWeeks.Width = new Unit(.5, UnitType.Inch);
            Controls.Add(spinNumWeeks);
            Controls.Add(new LiteralControl("</td><td>week(s) on:</td></tr></table>"));

            // checkbox list weekdays
            Controls.Add(new LiteralControl("<br/><br/>"));
            chklDayOfWeek = new CheckBoxList();
            chklDayOfWeek.Items.Add(new ListItem("Sunday", ScheduleWeekly.DAYS.SUNDAY.ToString()));
            chklDayOfWeek.Items.Add(new ListItem("Monday", ScheduleWeekly.DAYS.MONDAY.ToString()));
            chklDayOfWeek.Items.Add(new ListItem("Tuesday", ScheduleWeekly.DAYS.TUESDAY.ToString()));
            chklDayOfWeek.Items.Add(new ListItem("Wednesday", ScheduleWeekly.DAYS.WEDNESDAY.ToString()));
            chklDayOfWeek.Items.Add(new ListItem("Thursday", ScheduleWeekly.DAYS.THURSDAY.ToString()));
            chklDayOfWeek.Items.Add(new ListItem("Friday", ScheduleWeekly.DAYS.FRIDAY.ToString()));
            chklDayOfWeek.Items.Add(new ListItem("Saturday", ScheduleWeekly.DAYS.SATURDAY.ToString()));
            chklDayOfWeek.Items[1].Selected = true;
            chklDayOfWeek.RepeatColumns = 4;
            chklDayOfWeek.RepeatDirection = RepeatDirection.Horizontal;
            Controls.Add(chklDayOfWeek);
        }

        private ScheduleWeekly.DAYS getDays()
        {
            ScheduleWeekly.DAYS days = ScheduleWeekly.DAYS.NONE;

            foreach (ListItem item in chklDayOfWeek.Items)
            {
                if (item.Selected)
                {
                    string dayOfWeek = item.Value;
                    if (dayOfWeek == ScheduleWeekly.DAYS.SUNDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.SUNDAY;
                    }
                    else if (dayOfWeek == ScheduleWeekly.DAYS.MONDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.MONDAY;
                    }
                    else if (dayOfWeek == ScheduleWeekly.DAYS.TUESDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.TUESDAY;
                    }
                    else if (dayOfWeek == ScheduleWeekly.DAYS.WEDNESDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.WEDNESDAY;
                    }
                    else if (dayOfWeek == ScheduleWeekly.DAYS.THURSDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.THURSDAY;
                    }
                    else if (dayOfWeek == ScheduleWeekly.DAYS.FRIDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.FRIDAY;
                    }
                    else if (dayOfWeek == ScheduleWeekly.DAYS.SATURDAY.ToString())
                    {
                        days = days | ScheduleWeekly.DAYS.SATURDAY;
                    }
                    else
                    {
                        Exception ex = new Exception("An internal error in the weekly recurrence control caused an invalid day of week to be selected.");
                        ArrowUtils.LogFatalException(log, ex);
                        throw ex;
                    }
                }
            }

            return days;
        }
    }

    class YearlyRecurSel : Control
    {
        /// <summary>
        /// This is the first option, where you select a month and day.
        /// </summary>
        private RadioButton rbMonthDay;
        /// <summary>
        /// months listed in the first option (month and day)
        /// </summary>
        private DropDownList ddlMonthsMonthDay;
        /// <summary>
        /// day selection in the first option (month and day)
        /// </summary>
        //private ASPxSpinEdit spinDayMonthDay;
        private TextBox spinDayMonthDay;
        /// <summary>
        /// This is the second option, where you can select a day of month or week of month.
        /// </summary>
        private RadioButton rbDayOrWeek;
        /// <summary>
        /// list of indecies available for day or week of second option (day/week of month)
        /// </summary>
        private DropDownList ddlWeekIndex;
        /// <summary>
        /// list of day or week for second option (day/week of month)
        /// </summary>
        private DropDownList ddlDayOrWeek;
        /// <summary>
        /// list of months for second option (day/week of month)
        /// </summary>
        private DropDownList ddlMonthsDayOrWeek;
        /// <summary>
        /// the log
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(YearlyRecurSel));

        public SchedulePeriod ScheduleInfo
        {
            get
            {
                if (rbMonthDay.Checked)
                {
                    ScheduleYearly.MONTH month = ScheduleYearly.MONTH.APRIL;
                    int day = 1;
                    ScheduleYearly yearlyInfo = new ScheduleYearly(month, day);
                    return yearlyInfo;
                }
                else
                {
                    ScheduleYearly.MONTH month = ScheduleYearly.MONTH.APRIL;
                    ScheduleYearly.WEEK week = ScheduleMonth.WEEK.FIRST;
                    ScheduleYearly.WEEKDAY weekday = ScheduleMonth.WEEKDAY.MONDAY;
                    ScheduleYearly yearlyInfo = new ScheduleYearly(month, week, weekday);
                    return yearlyInfo;
                }
            }
        }

        public YearlyRecurSel()
        {
            if (log.IsDebugEnabled)
                log.Debug("creating yearly recursion selector");

            //Controls.Add(new LiteralControl("<br/><br/>Recur Yearly<br/>"));

            // first option month/day
            Controls.Add(new LiteralControl("<table><tr><td>"));
            // first radio
            rbMonthDay = new RadioButton();
            rbMonthDay.Checked = true;
            rbMonthDay.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
            rbMonthDay.AutoPostBack = true;
            Controls.Add(rbMonthDay);
            Controls.Add(new LiteralControl("</td><td>Every</td><td>"));
            // drop down months
            ddlMonthsMonthDay = new DropDownList();
            ddlMonthsMonthDay.Items.Add(new ListItem("January", ScheduleYearly.MONTH.JANUARY.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("February", ScheduleYearly.MONTH.FEBRUARY.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("March", ScheduleYearly.MONTH.MARCH.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("April", ScheduleYearly.MONTH.APRIL.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("May", ScheduleYearly.MONTH.MAY.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("June", ScheduleYearly.MONTH.JUNE.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("July", ScheduleYearly.MONTH.JULY.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("August", ScheduleYearly.MONTH.AUGUST.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("September", ScheduleYearly.MONTH.SEPTEMBER.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("October", ScheduleYearly.MONTH.OCTOBER.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("November", ScheduleYearly.MONTH.NOVEMBER.ToString()));
            ddlMonthsMonthDay.Items.Add(new ListItem("December", ScheduleYearly.MONTH.DECEMBER.ToString()));
            Controls.Add(ddlMonthsMonthDay);
            Controls.Add(new LiteralControl("</td><td>"));
            // spin days
            //spinDayMonthDay = new ASPxSpinEdit();
            spinDayMonthDay = new TextBox();
            //spinDayMonthDay.MinValue = 1;
            //spinDayMonthDay.MaxValue = 999;
            //spinDayMonthDay.NumberType = SpinEditNumberType.Integer;
            //spinDayMonthDay.MaxLength = 3;
            //spinDayMonthDay.Value = 1;
            spinDayMonthDay.Text = "1";
            spinDayMonthDay.Width = new Unit(.5, UnitType.Inch);
            Controls.Add(spinDayMonthDay);
            Controls.Add(new LiteralControl("</td></tr></table>"));

            // second option index/day or week/month
            Controls.Add(new LiteralControl("<table><tr><td>"));
            // radio button
            rbDayOrWeek = new RadioButton();
            rbDayOrWeek.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
            rbDayOrWeek.AutoPostBack = true;
            Controls.Add(rbDayOrWeek);
            Controls.Add(new LiteralControl("</td><td>Every</td><td>"));
            // ddl indecies
            ddlWeekIndex = new DropDownList();
            ddlWeekIndex.Items.Add(new ListItem("first", ScheduleYearly.WEEK.FIRST.ToString()));
            ddlWeekIndex.Items.Add(new ListItem("second", ScheduleYearly.WEEK.SECOND.ToString()));
            ddlWeekIndex.Items.Add(new ListItem("third", ScheduleYearly.WEEK.THIRD.ToString()));
            ddlWeekIndex.Items.Add(new ListItem("fourth", ScheduleYearly.WEEK.FOURTH.ToString()));
            ddlWeekIndex.Items.Add(new ListItem("last", ScheduleYearly.WEEK.LAST.ToString()));
            Controls.Add(ddlWeekIndex);
            // then [monday][every week]"of"[january]
            Controls.Add(new LiteralControl("</td><td>"));
            // list week, or every week
            ddlDayOrWeek = new DropDownList();
            ddlDayOrWeek.Items.Add(new ListItem("week", "week"));
            ddlDayOrWeek.Items.Add(new ListItem("every week", "every week"));
            Controls.Add(ddlDayOrWeek);
            Controls.Add(new LiteralControl("</td><td>of</td><td>"));
            // list months
            ddlMonthsDayOrWeek = new DropDownList();
            ddlMonthsDayOrWeek.Items.Add(new ListItem("January", ScheduleYearly.MONTH.JANUARY.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("February", ScheduleYearly.MONTH.FEBRUARY.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("March", ScheduleYearly.MONTH.MARCH.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("April", ScheduleYearly.MONTH.APRIL.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("May", ScheduleYearly.MONTH.MAY.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("June", ScheduleYearly.MONTH.JUNE.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("July", ScheduleYearly.MONTH.JULY.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("August", ScheduleYearly.MONTH.AUGUST.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("September", ScheduleYearly.MONTH.SEPTEMBER.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("October", ScheduleYearly.MONTH.OCTOBER.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("November", ScheduleYearly.MONTH.NOVEMBER.ToString()));
            ddlMonthsDayOrWeek.Items.Add(new ListItem("December", ScheduleYearly.MONTH.DECEMBER.ToString()));
            Controls.Add(ddlMonthsDayOrWeek);
            Controls.Add(new LiteralControl("</td></tr></table>"));
        }

        void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            if (log.IsDebugEnabled)
                log.Debug("handling radio checked changed");

            RadioButton rb = sender as RadioButton;
            if (rb == rbDayOrWeek)
            {
                rbMonthDay.Checked = false;
            }
            else if (rb == rbMonthDay)
            {
                rbDayOrWeek.Checked = false;
            }
        }
    }

    /// <summary>
    /// A form for setting the values that will be used for parameters in this schedule item.
    /// </summary>
    class ParameterValueSelector : Control, INamingContainer
    {
        /// <summary>
        /// Absolute path (url) to the report assembly
        /// </summary>
        public string ReportDefinitionPath 
        {
            get { return _reportAssemblyPath; }
            set { _reportAssemblyPath = value; } 
        }
        private string _reportAssemblyPath;

        /// <summary>
        /// get/set report asm guid
        /// </summary>
        public string ReportAssemblyGuid
        {
            get { return _reportAssemblyGuid; }
            set { _reportAssemblyGuid = value; }
        }
        private string _reportAssemblyGuid;

        public Guid templateGuid;
        public Guid templateSiteGuid;
        public TextBox[] variableValueTextBoxArray;
        public DropDownList[] variableDropDownListArray;
        public static ILog log = LogManager.GetLogger(typeof(ParameterValueSelector));

        public Table layoutTable;
        public Label[] variableNameLabelArray;
        public Label[] variableRequiredLabelArray;
        public Label[] variableTypeLabelArray;

        /// <summary>
        /// This is called from the scheduler web part - the template is unknown.
        /// </summary>
        public ParameterValueSelector()
            : base()
        {
            if (log.IsDebugEnabled)
                log.Debug("creating parameter value selector");

            // layout table
            layoutTable = ArrowUtils.createEmptyTable(2, 1, 41, 100);

            // header label
            ASPxLabel headerLabel = new ASPxLabel();
            headerLabel.Text = "Input or select report variable values.";
            headerLabel.ApplyStyle(ArrowUtils.headerStyle());
            layoutTable.Rows[0].Cells[0].Controls.Add(headerLabel);

            // BindRepeater was removed and replaced with ArrowUtils.createVarTable_runreport;
            // createVarTable_runreport takes in a number for the length of the table;
            // this behavior was designed to be similar to the variable input table in ReportGeneratorWebPart;
            // this design requires the max number of rows to be set upon creation - currently 20
            layoutTable.Rows[1].Cells[0].Controls.Add(ArrowUtils.createVarTable_runreport(null, 20, out variableValueTextBoxArray, out variableDropDownListArray,  out variableNameLabelArray, out variableRequiredLabelArray, out variableTypeLabelArray));

            Controls.Add(layoutTable);
        }
        /// <summary>
        /// This is called from ConfigureReport (custom actions) when the template is already known.
        /// </summary>
        /// <param name="_templateSiteGuid"></param>
        /// <param name="_templateGuid"></param>
        public ParameterValueSelector(Guid _templateSiteGuid, Guid _templateGuid)
            : base()
        {
            templateSiteGuid = _templateSiteGuid;
            templateGuid = _templateGuid;
            if (log.IsDebugEnabled)
                log.Debug("creating parameter value selector");

            // layout table
            layoutTable = ArrowUtils.createEmptyTable(2, 1, 41, 100);

            // header label
            ASPxLabel headerLabel = new ASPxLabel();
            headerLabel.Text = "Step 2: Input or select report variable values.";
            headerLabel.ApplyStyle(ArrowUtils.headerStyle());
            layoutTable.Rows[0].Cells[0].Controls.Add(headerLabel);

            string errorMessage = "";
            ReportDefinition rai = ArrowUtils.GetReportDefFromFile(templateSiteGuid, templateGuid, out errorMessage);
            int numVars = ArrowUtils.GetNumVarsForDisplay(rai);
            layoutTable.Rows[1].Cells[0].Controls.Add(ArrowUtils.createVarTable_runreport(rai, numVars, out variableValueTextBoxArray, out variableDropDownListArray, out variableNameLabelArray, out variableRequiredLabelArray, out variableTypeLabelArray));

            updateVarTable(rai);

            Controls.Add(layoutTable);
        }
        public ScheduleVarItemCollection getVars(Guid _templateSiteGuid, Guid _templateGuid)
        {
            // get the ReportDefinition
            string errorMessage = "";
            ReportDefinition rai = ArrowUtils.GetReportDefFromFile(_templateSiteGuid, _templateGuid, out errorMessage);

            // get WR_USER, WR_LOGIN
            const string WR_USER = "WR_USER";
            const string WR_LOGIN = "WR_LOGIN";
            SPUser currentUser = SPContext.Current.Web.CurrentUser;
            string userName = currentUser.Name ?? string.Empty;
            string userLogin = currentUser.LoginName ?? string.Empty;
            bool added_WR_USER = false;
            bool added_WR_LOGIN = false;
            
            // create var a var item from each parameter form in the repeater
            ScheduleVarItemCollection theVars = new ScheduleVarItemCollection();
            for (int i = 0; i < rai.variableNameArray.Length; i++)
            {
                ScheduleVarItem varItem = null;// = new ScheduleVarItem(
                // parameter name
                string parmName = rai.variableNameArray[i];

                // WR_LOGIN, WR_USER are reserved; if they are found here, overwrite them
                if (parmName.Equals(WR_USER))
                {
                    theVars.Add(new ScheduleVarItem(WR_USER, userName));
                    added_WR_USER = true;
                    continue;
                }
                else if (parmName.Equals(WR_LOGIN))
                {
                    theVars.Add(new ScheduleVarItem(WR_LOGIN, userLogin));
                    added_WR_LOGIN = true;
                    continue;
                }

                string varType = rai.variableTypeArray[i].ToLower();
                bool isSelect = varType.Equals("select");
                if (!isSelect || variableDropDownListArray[i].Items.Count == 0) // textbox
                {
                    // parameter value for text boxes
                    TextBox varText = variableValueTextBoxArray[i];
                    object varVal = varText.Text.Trim();
                    switch (varType)
                    {
                        case "currency":
                            varVal = Convert.ToDecimal(varVal);
                            break;
                        case "date":
                            varVal = Convert.ToDateTime(varVal);
                            break;
                        case "integer":
                            varVal = Convert.ToInt64(varVal);
                            break;
                        case "number":
                            varVal = Convert.ToDouble(varVal);
                            break;
                    }
                    varItem = new ScheduleVarItem(parmName, varVal);
                }
                else // select drop-down list
                {
                    // parameter value for select ddls
                    DropDownList varSelect = variableDropDownListArray[i];
                    varItem = new ScheduleVarItem(parmName, varSelect.SelectedValue);
                }
                theVars.Add(varItem);
            }

            // add WR_USER, WR_LOGIN
            if (!added_WR_LOGIN)
                theVars.Add(new ScheduleVarItem(WR_LOGIN, userLogin));
            if (!added_WR_USER)
                theVars.Add(new ScheduleVarItem(WR_USER, userName));

            return theVars;
        }
        /// <summary>
        /// This updates the variable input controls based on the current ReportDefinition.
        /// </summary>
        /// <param name="rai"></param>
        private void updateVarTable(ReportDefinition rai)
        {
            // go through the ReportDefinition, and "enable" a control for each variable
            if (rai != null)
            {
                int numVars = ArrowUtils.GetNumVarsForDisplay(rai);
                const string WR_LOGIN = "WR_LOGIN";
                const string WR_USER = "WR_USER";
                int ind_WR_LOGIN = -1;
                int ind_WR_USER = -1;
                for (int i = 0; i < rai.variableNameArray.Length; i++)
                {
                    if (rai.variableNameArray[i].Equals(WR_LOGIN))
                        ind_WR_LOGIN = i;
                    else if (rai.variableNameArray[i].Equals(WR_USER))
                        ind_WR_USER = i;
                }

                #region enable variable input controls
                int raiVarInd = 0;
                for (int i = 0; i < numVars; i++)
                {
                    while (raiVarInd == ind_WR_LOGIN || raiVarInd == ind_WR_USER)
                    {
                        raiVarInd++;
                    }
                    if (raiVarInd >= rai.variableNameArray.Length)
                    {
                        // this shouldn't happen
                        Trap.trap();
                        if (log.IsDebugEnabled)
                            log.Debug("raiVarInd >= rai.variableNameArray.Length returned true");
                    }

                    variableNameLabelArray[i].Visible = true;
                    variableNameLabelArray[i].Text = rai.variableNameArray[raiVarInd];

                    variableRequiredLabelArray[i].Visible = true;
                    variableRequiredLabelArray[i].Text = rai.variableRequiredArray[raiVarInd];

                    variableTypeLabelArray[i].Visible = true;
                    variableTypeLabelArray[i].Text = rai.variableTypeArray[raiVarInd];

                    if (variableTypeLabelArray[i].Text.ToLower() == "select")
                    {

                        //do select and add items;
                        string nameToSearch = rai.variableDataNameArray[raiVarInd];
                        DataDefinition[] datasources = rai.getDataSources();
                        DataDefinition dataDef = null;
                        for (int j = 0; j < rai.dataSourceNameArray.Length; j++)
                        {
                            if (rai.dataSourceNameArray[j] == nameToSearch)
                            {
                                dataDef = datasources[j];
                                break;
                            }
                        }
                        if (dataDef == null)
                            throw new FileNotFoundException(string.Format("Could not find datadef entry named {0}.", nameToSearch));

                        object[][] queryResult = ArrowUtils.getSelectParameterObjects(rai.variableDataNameArray[raiVarInd], dataDef, rai.variableSelectCountArray[raiVarInd], rai.variableSelectAllArray[raiVarInd]);
                        if (queryResult.Length > 25)
                        {
                            //display a textbox instead of a drop down, do validatio on next button click
                            variableValueTextBoxArray[i].Visible = true;
                            variableValueTextBoxArray[i].Text = rai.variableDefaultArray[raiVarInd];
                            variableDropDownListArray[i].Items.Clear();
                            variableDropDownListArray[i].Visible = false;
                        }
                        else
                        {
                            variableDropDownListArray[i].Visible = true;
                            variableValueTextBoxArray[i].Text = string.Empty;
                            variableValueTextBoxArray[i].Visible = false;
                            foreach (object[] ob in queryResult)
                            {
                                string input = ob[0].ToString();
                                variableDropDownListArray[i].Items.Add(new ListItem(input, input));
                            }
                            if (variableDropDownListArray[i].Items.Count == 0)
                            {
                                variableDropDownListArray[i].Items.Add(new ListItem("<none>", ""));
                            }
                            //set default value
                            string defaultValue = rai.variableDefaultArray[raiVarInd];
                            for (int j = 0; j < variableDropDownListArray[i].Items.Count; j++)
                            {
                                if (variableDropDownListArray[i].Items[j].Value == defaultValue)
                                {
                                    variableDropDownListArray[i].SelectedIndex = j;
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        variableValueTextBoxArray[i].Visible = true;
                        variableValueTextBoxArray[i].Text = rai.variableDefaultArray[raiVarInd];
                        variableDropDownListArray[i].Items.Clear();
                        variableDropDownListArray[i].Visible = false;
                    }
                    if ("True".Equals(rai.variableHiddenArray[raiVarInd]))
                    {
                        variableTypeLabelArray[i].Visible = false;
                        variableDropDownListArray[i].Visible = false;
                        variableValueTextBoxArray[i].Visible = false;
                        variableNameLabelArray[i].Visible = false;
                        variableRequiredLabelArray[i].Visible = false;
                        variableNameLabelArray[i].Visible = false;
                    }

                    raiVarInd++;
                }
                #endregion enable variable input controls
            }
        }
        /// <summary>
        /// This updates the variable input controls based on the current ReportDefinition.
        /// </summary>
        public void updateVarTable()
        {
            // get the ReportDefinition
            string errorMessage = "";
            ReportDefinition rai = ArrowUtils.GetReportDefFromFile(templateSiteGuid, templateGuid, out errorMessage);

            updateVarTable(rai);
        }
    }

    #region TemplateMapItem
    /// <summary>
    /// A single key-value map pair used when running a report. Each pair must be set before the report is run.
    /// </summary>
    internal sealed class TemplateMapItem
    {

        /// <summary>
        /// The type for the value. Warning these numbers match values for the dropdownlist - don't change.
        /// </summary>
        public enum ValueType : int
        {
            /// <summary>
            /// Stored as a float ex: "1.23" but will be parsed as a money amount.
            /// </summary>
            CURRENCY = 0,
            /// <summary>
            /// Stored as a date ex: "01/02/2003" but will be prompted with a calander.
            /// </summary>
            DATE = 1,
            /// <summary>
            /// Must be an integer.
            /// </summary>
            INTEGER = 2,
            /// <summary>
            /// Is a double in the form "1.23"
            /// </summary>
            NUMBER = 3,
            /// <summary>
            /// Is one of the values returned in column 0 from the select.
            /// </summary>
            SELECT = 4,
            /// <summary>
            /// Any text.
            /// </summary>
            TEXT = 5
        };

        /// <summary>
        /// Creates a copy.
        /// </summary>
        /// <param name="src">The source object.</param>
        public TemplateMapItem(TemplateMapItem src)
        {
            Name = src.Name;
            Value = src.Value;
            Required = src.Required;
            Type = src.Type;
            Description = src.Description;
        }

        /// <summary>
        /// The key part of the key-value pair.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The value part of the key-value pair.
        /// </summary>
        public string Value { get; set; }

        /// <summary>
        /// True if this must have a value to run the report.
        /// </summary>
        public bool Required
        {
            get { return required; }
            set { required = value; }
        }
        private bool required;

        /// <summary>
        /// What type the value is. All values are stored as a string, this is used for prompting and validation of 
        /// the returned value only.
        /// </summary>
        public ValueType Type
        {
            get { return type; }
            set { type = value; }
        }
        private ValueType type;

        /// <summary>
        /// A description of what this map value is used for.
        /// </summary>
        public string Description { get; set; }

        public DropDownList DdlSelectOptions { get; set; }

        /// <summary>
        /// Create a TemplateMap object.
        /// </summary>
        public TemplateMapItem()
        {
            Type = ValueType.TEXT;
            Value = string.Empty;
        }
    }

    /// <summary>
    /// A collection of TemplateMapItem objects.
    /// </summary>
    internal sealed class TemplateMapItemCollection : Collection<TemplateMapItem>
    {

        /// <summary>
        /// Return the element with this name. Return null if don't have it.
        /// </summary>
        /// <param name="name">The name of this map variable.</param>
        public TemplateMapItem FindByName(string name)
        {
            foreach (TemplateMapItem tmi in this)
                if (tmi.Name == name)
                    return tmi;
            return null;
        }
    }
    #endregion TemplateMapItem
}
